vidfom commited on
Commit
f2b53cf
·
verified ·
1 Parent(s): 289daab

Upload folder using huggingface_hub (part 6)

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 +25 -0
  2. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/api/bizyair.py +372 -0
  3. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/api/fluxai.py +51 -0
  4. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/api/stability.py +200 -0
  5. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/cache.py +87 -0
  6. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/chooser.py +153 -0
  7. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/colorfix.py +115 -0
  8. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/conditioning.py +57 -0
  9. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/controlnet.py +93 -0
  10. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/dynthres_core.py +167 -0
  11. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/easing.py +27 -0
  12. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/gradual_latent_hires_fix.py +273 -0
  13. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/image.py +227 -0
  14. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/lllite.py +237 -0
  15. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/loader.py +570 -0
  16. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/log.py +77 -0
  17. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/math.py +148 -0
  18. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/messages.py +55 -0
  19. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/model.py +58 -0
  20. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/sampler.py +1053 -0
  21. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/styleAlign.py +148 -0
  22. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/translate.py +247 -0
  23. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/utils.py +300 -0
  24. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/wildcards.py +476 -0
  25. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/xyplot.py +719 -0
  26. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/ben/model.py +1310 -0
  27. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/bitsandbytes_NF4/__init__.py +167 -0
  28. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/briaai/__init__.py +0 -0
  29. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/briaai/rembg.py +475 -0
  30. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__init__.py +822 -0
  31. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/__init__.cpython-312.pyc +0 -0
  32. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/model.cpython-312.pyc +0 -0
  33. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/model_patch.cpython-312.pyc +0 -0
  34. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/powerpaint_utils.cpython-312.pyc +0 -0
  35. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/unet_2d_blocks.cpython-312.pyc +3 -0
  36. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/unet_2d_condition.cpython-312.pyc +0 -0
  37. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/config/brushnet.json +58 -0
  38. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/config/brushnet_xl.json +63 -0
  39. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/config/powerpaint.json +57 -0
  40. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/model.py +1688 -0
  41. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/model_patch.py +137 -0
  42. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/powerpaint_utils.py +467 -0
  43. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/unet_2d_blocks.py +0 -0
  44. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/unet_2d_condition.py +1359 -0
  45. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/__init__.py +2 -0
  46. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/__pycache__/__init__.cpython-312.pyc +0 -0
  47. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/config.py +120 -0
  48. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/pixArt/LICENSE-Pixart +661 -0
  49. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/pixArt/__init__.py +0 -0
  50. ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/pixArt/__pycache__/__init__.cpython-312.pyc +0 -0
.gitattributes CHANGED
@@ -46,3 +46,28 @@ ComfyUI/comfy_api_nodes/__pycache__/nodes_bytedance.cpython-312.pyc filter=lfs d
46
  ComfyUI/comfy_api_nodes/__pycache__/nodes_kling.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
47
  ComfyUI/comfy_api_nodes/apis/__pycache__/__init__.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
48
  ComfyUI/comfy_extras/__pycache__/nodes_gaussian_splat.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  ComfyUI/comfy_api_nodes/__pycache__/nodes_kling.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
47
  ComfyUI/comfy_api_nodes/apis/__pycache__/__init__.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
48
  ComfyUI/comfy_extras/__pycache__/nodes_gaussian_splat.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
49
+ ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/unet_2d_blocks.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
50
+ ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/kolors/chatglm/tokenizer/vocab.txt filter=lfs diff=lfs merge=lfs -text
51
+ ComfyUI/custom_nodes/ComfyUI-Easy-Use/resources/OpenSans-Medium.ttf filter=lfs diff=lfs merge=lfs -text
52
+ ComfyUI/custom_nodes/ComfyUI-Easy-Use/resources/wenquan.ttf filter=lfs diff=lfs merge=lfs -text
53
+ ComfyUI/custom_nodes/ComfyUI-KJNodes/docs/images/2024-04-03_20_49_29-ComfyUI.png filter=lfs diff=lfs merge=lfs -text
54
+ ComfyUI/custom_nodes/ComfyUI-KJNodes/fonts/FreeMono.ttf filter=lfs diff=lfs merge=lfs -text
55
+ ComfyUI/custom_nodes/ComfyUI-KJNodes/fonts/FreeMonoBoldOblique.otf filter=lfs diff=lfs merge=lfs -text
56
+ ComfyUI/custom_nodes/ComfyUI-KJNodes/fonts/TTNorms-Black.otf filter=lfs diff=lfs merge=lfs -text
57
+ ComfyUI/custom_nodes/ComfyUI-KJNodes/nodes/__pycache__/image_nodes.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
58
+ ComfyUI/custom_nodes/ComfyUI-KJNodes/nodes/__pycache__/ltxv_nodes.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
59
+ ComfyUI/custom_nodes/ComfyUI-KJNodes/nodes/__pycache__/model_optimization_nodes.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
60
+ ComfyUI/custom_nodes/ComfyUI-KJNodes/nodes/__pycache__/nodes.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
61
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/base[[:space:]]model[[:space:]]image.png filter=lfs diff=lfs merge=lfs -text
62
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/buildings[[:space:]]ff.png filter=lfs diff=lfs merge=lfs -text
63
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/buildings.mp4 filter=lfs diff=lfs merge=lfs -text
64
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/distilled[[:space:]]image.png filter=lfs diff=lfs merge=lfs -text
65
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/hdr_input_video.mp4 filter=lfs diff=lfs merge=lfs -text
66
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/ingredients_input.jpg filter=lfs diff=lfs merge=lfs -text
67
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/inpainting_input.mp4 filter=lfs diff=lfs merge=lfs -text
68
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/instant_shave_input.mp4 filter=lfs diff=lfs merge=lfs -text
69
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/lipdub_input.mp4 filter=lfs diff=lfs merge=lfs -text
70
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/motion_track_input.jpg filter=lfs diff=lfs merge=lfs -text
71
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/outpainting_input.mp4 filter=lfs diff=lfs merge=lfs -text
72
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/example_workflows/assets/spatial_upscale_4x_input.mp4 filter=lfs diff=lfs merge=lfs -text
73
+ ComfyUI/custom_nodes/ComfyUI-LTXVideo/gemma_configs/tokenizer.json filter=lfs diff=lfs merge=lfs -text
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/api/bizyair.py ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import yaml
2
+ import pathlib
3
+ import base64
4
+ import io
5
+ import json
6
+ import os
7
+ import pickle
8
+ import zlib
9
+ import urllib.parse
10
+ import urllib.request
11
+ import urllib.error
12
+ from enum import Enum
13
+ from functools import singledispatch
14
+ from typing import Any, List, Union
15
+
16
+ import numpy as np
17
+ import torch
18
+ from PIL import Image
19
+
20
+ root_path = pathlib.Path(__file__).parent.parent.parent.parent
21
+ config_path = os.path.join(root_path, 'config.yaml')
22
+
23
+ class BizyAIRAPI:
24
+ def __init__(self):
25
+ self.base_url = 'https://bizyair-api.siliconflow.cn/x/v1'
26
+ self.api_key = None
27
+
28
+
29
+ def getAPIKey(self):
30
+ if self.api_key is None:
31
+ if os.path.isfile(config_path):
32
+ with open(config_path, 'r') as f:
33
+ data = yaml.load(f, Loader=yaml.FullLoader)
34
+ if 'BIZYAIR_API_KEY' not in data:
35
+ raise Exception("Please add BIZYAIR_API_KEY to config.yaml")
36
+ self.api_key = data['BIZYAIR_API_KEY']
37
+ else:
38
+ raise Exception("Please add config.yaml to root path")
39
+ return self.api_key
40
+
41
+ def send_post_request(self, url, payload, headers):
42
+ try:
43
+ data = json.dumps(payload).encode("utf-8")
44
+ req = urllib.request.Request(url, data=data, headers=headers, method="POST")
45
+ with urllib.request.urlopen(req) as response:
46
+ response_data = response.read().decode("utf-8")
47
+ return response_data
48
+ except urllib.error.URLError as e:
49
+ if "Unauthorized" in str(e):
50
+ raise Exception(
51
+ "Key is invalid, please refer to https://cloud.siliconflow.cn to get the API key.\n"
52
+ "If you have the key, please click the 'BizyAir Key' button at the bottom right to set the key."
53
+ )
54
+ else:
55
+ raise Exception(
56
+ f"Failed to connect to the server: {e}, if you have no key, "
57
+ )
58
+
59
+ # joycaption
60
+ def joyCaption(self, payload, image, apikey_override=None, API_URL='/supernode/joycaption2'):
61
+ if apikey_override is not None:
62
+ api_key = apikey_override
63
+ else:
64
+ api_key = self.getAPIKey()
65
+ url = f"{self.base_url}{API_URL}"
66
+ print('Sending request to:', url)
67
+ auth = f"Bearer {api_key}"
68
+ headers = {
69
+ "accept": "application/json",
70
+ "content-type": "application/json",
71
+ "authorization": auth,
72
+ }
73
+ input_image = encode_data(image, disable_image_marker=True)
74
+ payload["image"] = input_image
75
+
76
+ ret: str = self.send_post_request(url=url, payload=payload, headers=headers)
77
+ ret = json.loads(ret)
78
+
79
+ try:
80
+ if "result" in ret:
81
+ ret = json.loads(ret["result"])
82
+ except Exception as e:
83
+ raise Exception(f"Unexpected response: {ret} {e=}")
84
+
85
+ if ret["type"] == "error":
86
+ raise Exception(ret["message"])
87
+
88
+ msg = ret["data"]
89
+ if msg["type"] not in ("comfyair", "bizyair",):
90
+ raise Exception(f"Unexpected response type: {msg}")
91
+
92
+ caption = msg["data"]
93
+
94
+ return caption
95
+
96
+ bizyairAPI = BizyAIRAPI()
97
+
98
+
99
+
100
+ BIZYAIR_DEBUG = True
101
+ # Marker to identify base64-encoded tensors
102
+ TENSOR_MARKER = "TENSOR:"
103
+ IMAGE_MARKER = "IMAGE:"
104
+
105
+
106
+ class TaskStatus(Enum):
107
+ PENDING = "pending"
108
+ PROCESSING = "processing"
109
+ COMPLETED = "completed"
110
+
111
+
112
+ def convert_image_to_rgb(image: Image.Image) -> Image.Image:
113
+ if image.mode != "RGB":
114
+ return image.convert("RGB")
115
+ return image
116
+
117
+
118
+ def encode_image_to_base64(
119
+ image: Image.Image, format: str = "png", quality: int = 100, lossless=False
120
+ ) -> str:
121
+ image = convert_image_to_rgb(image)
122
+ with io.BytesIO() as output:
123
+ image.save(output, format=format, quality=quality, lossless=lossless)
124
+ output.seek(0)
125
+ img_bytes = output.getvalue()
126
+ if BIZYAIR_DEBUG:
127
+ print(f"encode_image_to_base64: {format_bytes(len(img_bytes))}")
128
+ return base64.b64encode(img_bytes).decode("utf-8")
129
+
130
+
131
+ def decode_base64_to_np(img_data: str, format: str = "png") -> np.ndarray:
132
+ img_bytes = base64.b64decode(img_data)
133
+ if BIZYAIR_DEBUG:
134
+ print(f"decode_base64_to_np: {format_bytes(len(img_bytes))}")
135
+ with io.BytesIO(img_bytes) as input_buffer:
136
+ img = Image.open(input_buffer)
137
+ # https://github.com/comfyanonymous/ComfyUI/blob/a178e25912b01abf436eba1cfaab316ba02d272d/nodes.py#L1511
138
+ img = img.convert("RGB")
139
+ return np.array(img)
140
+
141
+
142
+ def decode_base64_to_image(img_data: str) -> Image.Image:
143
+ img_bytes = base64.b64decode(img_data)
144
+ with io.BytesIO(img_bytes) as input_buffer:
145
+ img = Image.open(input_buffer)
146
+ if BIZYAIR_DEBUG:
147
+ format_info = img.format.upper() if img.format else "Unknown"
148
+ print(f"decode image format: {format_info}")
149
+ return img
150
+
151
+
152
+ def format_bytes(num_bytes: int) -> str:
153
+ """
154
+ Converts a number of bytes to a human-readable string with units (B, KB, or MB).
155
+
156
+ :param num_bytes: The number of bytes to convert.
157
+ :return: A string representing the number of bytes in a human-readable format.
158
+ """
159
+ if num_bytes < 1024:
160
+ return f"{num_bytes} B"
161
+ elif num_bytes < 1024 * 1024:
162
+ return f"{num_bytes / 1024:.2f} KB"
163
+ else:
164
+ return f"{num_bytes / (1024 * 1024):.2f} MB"
165
+
166
+
167
+ def _legacy_encode_comfy_image(image: torch.Tensor, image_format="png") -> str:
168
+ input_image = image.cpu().detach().numpy()
169
+ i = 255.0 * input_image[0]
170
+ input_image = np.clip(i, 0, 255).astype(np.uint8)
171
+ base64ed_image = encode_image_to_base64(
172
+ Image.fromarray(input_image), format=image_format
173
+ )
174
+ return base64ed_image
175
+
176
+
177
+ def _legacy_decode_comfy_image(
178
+ img_data: Union[List, str], image_format="png"
179
+ ) -> torch.tensor:
180
+ if isinstance(img_data, List):
181
+ decoded_imgs = [decode_comfy_image(x, old_version=True) for x in img_data]
182
+
183
+ combined_imgs = torch.cat(decoded_imgs, dim=0)
184
+ return combined_imgs
185
+
186
+ out = decode_base64_to_np(img_data, format=image_format)
187
+ out = np.array(out).astype(np.float32) / 255.0
188
+ output = torch.from_numpy(out)[None,]
189
+ return output
190
+
191
+
192
+ def _new_encode_comfy_image(images: torch.Tensor, image_format="WEBP", **kwargs) -> str:
193
+ """https://docs.comfy.org/essentials/custom_node_snippets#save-an-image-batch
194
+ Encode a batch of images to base64 strings.
195
+
196
+ Args:
197
+ images (torch.Tensor): A batch of images.
198
+ image_format (str, optional): The format of the images. Defaults to "WEBP".
199
+
200
+ Returns:
201
+ str: A JSON string containing the base64-encoded images.
202
+ """
203
+ results = {}
204
+ for batch_number, image in enumerate(images):
205
+ i = 255.0 * image.cpu().numpy()
206
+ img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
207
+ base64ed_image = encode_image_to_base64(img, format=image_format, **kwargs)
208
+ results[batch_number] = base64ed_image
209
+
210
+ return json.dumps(results)
211
+
212
+
213
+ def _new_decode_comfy_image(img_datas: str, image_format="WEBP") -> torch.tensor:
214
+ """
215
+ Decode a batch of base64-encoded images.
216
+
217
+ Args:
218
+ img_datas (str): A JSON string containing the base64-encoded images.
219
+ image_format (str, optional): The format of the images. Defaults to "WEBP".
220
+
221
+ Returns:
222
+ torch.Tensor: A tensor containing the decoded images.
223
+ """
224
+ img_datas = json.loads(img_datas)
225
+
226
+ decoded_imgs = []
227
+ for img_data in img_datas.values():
228
+ decoded_image = decode_base64_to_np(img_data, format=image_format)
229
+ decoded_image = np.array(decoded_image).astype(np.float32) / 255.0
230
+ decoded_imgs.append(torch.from_numpy(decoded_image)[None,])
231
+
232
+ return torch.cat(decoded_imgs, dim=0)
233
+
234
+
235
+ def encode_comfy_image(
236
+ image: torch.Tensor, image_format="WEBP", old_version=False, lossless=False
237
+ ) -> str:
238
+ if old_version:
239
+ return _legacy_encode_comfy_image(image, image_format)
240
+ return _new_encode_comfy_image(image, image_format, lossless=lossless)
241
+
242
+
243
+ def decode_comfy_image(
244
+ img_data: Union[List, str], image_format="WEBP", old_version=False
245
+ ) -> torch.tensor:
246
+ if old_version:
247
+ return _legacy_decode_comfy_image(img_data, image_format)
248
+ return _new_decode_comfy_image(img_data, image_format)
249
+
250
+
251
+ def tensor_to_base64(tensor: torch.Tensor, compress=True) -> str:
252
+ tensor_np = tensor.cpu().detach().numpy()
253
+
254
+ tensor_bytes = pickle.dumps(tensor_np)
255
+ if compress:
256
+ tensor_bytes = zlib.compress(tensor_bytes)
257
+
258
+ tensor_b64 = base64.b64encode(tensor_bytes).decode("utf-8")
259
+ return tensor_b64
260
+
261
+
262
+ def base64_to_tensor(tensor_b64: str, compress=True) -> torch.Tensor:
263
+ tensor_bytes = base64.b64decode(tensor_b64)
264
+
265
+ if compress:
266
+ tensor_bytes = zlib.decompress(tensor_bytes)
267
+
268
+ tensor_np = pickle.loads(tensor_bytes)
269
+
270
+ tensor = torch.from_numpy(tensor_np)
271
+ return tensor
272
+
273
+
274
+ @singledispatch
275
+ def decode_data(input, old_version=False):
276
+ raise NotImplementedError(f"Unsupported type: {type(input)}")
277
+
278
+
279
+ @decode_data.register(int)
280
+ @decode_data.register(float)
281
+ @decode_data.register(bool)
282
+ @decode_data.register(type(None))
283
+ def _(input, **kwargs):
284
+ return input
285
+
286
+
287
+ @decode_data.register(dict)
288
+ def _(input, **kwargs):
289
+ return {k: decode_data(v, **kwargs) for k, v in input.items()}
290
+
291
+
292
+ @decode_data.register(list)
293
+ def _(input, **kwargs):
294
+ return [decode_data(x, **kwargs) for x in input]
295
+
296
+
297
+ @decode_data.register(str)
298
+ def _(input: str, **kwargs):
299
+ if input.startswith(TENSOR_MARKER):
300
+ tensor_b64 = input[len(TENSOR_MARKER) :]
301
+ return base64_to_tensor(tensor_b64)
302
+ elif input.startswith(IMAGE_MARKER):
303
+ tensor_b64 = input[len(IMAGE_MARKER) :]
304
+ old_version = kwargs.get("old_version", False)
305
+ return decode_comfy_image(tensor_b64, old_version=old_version)
306
+ return input
307
+
308
+
309
+ @singledispatch
310
+ def encode_data(output, disable_image_marker=False, old_version=False):
311
+ raise NotImplementedError(f"Unsupported type: {type(output)}")
312
+
313
+
314
+ @encode_data.register(dict)
315
+ def _(output, **kwargs):
316
+ return {k: encode_data(v, **kwargs) for k, v in output.items()}
317
+
318
+
319
+ @encode_data.register(list)
320
+ def _(output, **kwargs):
321
+ return [encode_data(x, **kwargs) for x in output]
322
+
323
+
324
+ def is_image_tensor(tensor) -> bool:
325
+ """https://docs.comfy.org/essentials/custom_node_datatypes#image
326
+
327
+ Check if the given tensor is in the format of an IMAGE (shape [B, H, W, C] where C=3).
328
+
329
+ `Args`:
330
+ tensor (torch.Tensor): The tensor to check.
331
+
332
+ `Returns`:
333
+ bool: True if the tensor is in the IMAGE format, False otherwise.
334
+ """
335
+ try:
336
+ if not isinstance(tensor, torch.Tensor):
337
+ return False
338
+
339
+ if len(tensor.shape) != 4:
340
+ return False
341
+
342
+ B, H, W, C = tensor.shape
343
+ if C != 3:
344
+ return False
345
+
346
+ return True
347
+ except:
348
+ return False
349
+
350
+
351
+ @encode_data.register(torch.Tensor)
352
+ def _(output, **kwargs):
353
+ if is_image_tensor(output) and not kwargs.get("disable_image_marker", False):
354
+ old_version = kwargs.get("old_version", False)
355
+ lossless = kwargs.get("lossless", True)
356
+ return IMAGE_MARKER + encode_comfy_image(
357
+ output, image_format="WEBP", old_version=old_version, lossless=lossless
358
+ )
359
+ return TENSOR_MARKER + tensor_to_base64(output)
360
+
361
+
362
+ @encode_data.register(int)
363
+ @encode_data.register(float)
364
+ @encode_data.register(bool)
365
+ @encode_data.register(type(None))
366
+ def _(output, **kwargs):
367
+ return output
368
+
369
+
370
+ @encode_data.register(str)
371
+ def _(output, **kwargs):
372
+ return output
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/api/fluxai.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import yaml
4
+ import requests
5
+ import pathlib
6
+ from aiohttp import web
7
+
8
+ root_path = pathlib.Path(__file__).parent.parent.parent.parent
9
+ config_path = os.path.join(root_path,'config.yaml')
10
+ class FluxAIAPI:
11
+ def __init__(self):
12
+ self.api_url = "https://fluxaiimagegenerator.com/api"
13
+ self.origin = "https://fluxaiimagegenerator.com"
14
+ self.user_agent = None
15
+ self.cookie = None
16
+
17
+ def promptGenerate(self, text, cookies=None):
18
+ cookie = self.cookie if cookies is None else cookies
19
+ if cookie is None:
20
+ if os.path.isfile(config_path):
21
+ with open(config_path, 'r') as f:
22
+ data = yaml.load(f, Loader=yaml.FullLoader)
23
+ if 'FLUXAI_COOKIE' not in data:
24
+ raise Exception("Please add FLUXAI_COOKIE to config.yaml")
25
+ if "FLUXAI_USER_AGENT" in data:
26
+ self.user_agent = data["FLUXAI_USER_AGENT"]
27
+ self.cookie = cookie = data['FLUXAI_COOKIE']
28
+
29
+ headers = {
30
+ "Cookie": cookie,
31
+ "Referer": "https://fluxaiimagegenerator.com/flux-prompt-generator",
32
+ "Origin": self.origin,
33
+ "Content-Type": "application/json",
34
+ }
35
+ if self.user_agent is not None:
36
+ headers['User-Agent'] = self.user_agent
37
+
38
+ url = self.api_url + '/prompt'
39
+ json = {
40
+ "prompt": text
41
+ }
42
+
43
+ response = requests.post(url, json=json, headers=headers)
44
+ res = response.json()
45
+ if "error" in res:
46
+ return res['error']
47
+ elif "data" in res and "prompt" in res['data']:
48
+ return res['data']['prompt']
49
+
50
+ fluxaiAPI = FluxAIAPI()
51
+
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/api/stability.py ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import yaml
4
+ import requests
5
+ import pathlib
6
+ from aiohttp import web
7
+ from server import PromptServer
8
+ from ..image import tensor2pil, pil2tensor, image2base64, pil2byte
9
+ from ..log import log_node_error
10
+
11
+
12
+ root_path = pathlib.Path(__file__).parent.parent.parent.parent
13
+ config_path = os.path.join(root_path,'config.yaml')
14
+ default_key = [{'name':'Default', 'key':''}]
15
+
16
+
17
+ class StabilityAPI:
18
+ def __init__(self):
19
+ self.api_url = "https://api.stability.ai"
20
+ self.api_keys = None
21
+ self.api_current = 0
22
+ self.user_info = {}
23
+
24
+ def getErrors(self, code):
25
+ errors = {
26
+ 400: "Bad Request",
27
+ 403: "ApiKey Forbidden",
28
+ 413: "Your request was larger than 10MiB.",
29
+ 429: "You have made more than 150 requests in 10 seconds.",
30
+ 500: "Internal Server Error",
31
+ }
32
+ return errors.get(code, "Unknown Error")
33
+
34
+ def getAPIKeys(self):
35
+ if os.path.isfile(config_path):
36
+ with open(config_path, 'r') as f:
37
+ data = yaml.load(f, Loader=yaml.FullLoader)
38
+ if not data:
39
+ data = {'STABILITY_API_KEY': default_key, 'STABILITY_API_DEFAULT':0}
40
+ with open(config_path, 'w') as f:
41
+ yaml.dump(data, f)
42
+ if 'STABILITY_API_KEY' not in data:
43
+ data['STABILITY_API_KEY'] = default_key
44
+ data['STABILITY_API_DEFAULT'] = 0
45
+ with open(config_path, 'w') as f:
46
+ yaml.dump(data, f)
47
+ api_keys = data['STABILITY_API_KEY']
48
+ self.api_current = data['STABILITY_API_DEFAULT']
49
+ self.api_keys = api_keys
50
+ return api_keys
51
+ else:
52
+ # create a yaml file
53
+ with open(config_path, 'w') as f:
54
+ data = {'STABILITY_API_KEY': default_key, 'STABILITY_API_DEFAULT':0}
55
+ yaml.dump(data, f)
56
+ return data['STABILITY_API_KEY']
57
+ pass
58
+
59
+ def setAPIKeys(self, api_keys):
60
+ if len(api_keys) > 0:
61
+ self.api_keys = api_keys
62
+ # load and save the yaml file
63
+ with open(config_path, 'r') as f:
64
+ data = yaml.load(f, Loader=yaml.FullLoader)
65
+ data['STABILITY_API_KEY'] = api_keys
66
+ with open(config_path, 'w') as f:
67
+ yaml.dump(data, f)
68
+ return True
69
+
70
+ def setAPIDefault(self, current):
71
+ if current is not None:
72
+ self.api_current = current
73
+ # load and save the yaml file
74
+ with open(config_path, 'r') as f:
75
+ data = yaml.load(f, Loader=yaml.FullLoader)
76
+ data['STABILITY_API_DEFAULT'] = current
77
+ with open(config_path, 'w') as f:
78
+ yaml.dump(data, f)
79
+ return True
80
+
81
+ def generate_sd3_image(self, prompt, negative_prompt, aspect_ratio, model, seed, mode='text-to-image', image=None, strength=1, output_format='png', node_name='easy stableDiffusion3API'):
82
+ url = f"{self.api_url}/v2beta/stable-image/generate/sd3"
83
+ api_key = self.api_keys[self.api_current]['key']
84
+ files = None
85
+ data = {
86
+ "prompt": prompt,
87
+ "mode": mode,
88
+ "model": model,
89
+ "seed": seed,
90
+ "output_format": output_format,
91
+ }
92
+ if model == 'sd3':
93
+ data['negative_prompt'] = negative_prompt
94
+
95
+ if mode == 'text-to-image':
96
+ files = {"none": ''}
97
+ data['aspect_ratio'] = aspect_ratio
98
+ elif mode == 'image-to-image':
99
+ pil_image = tensor2pil(image)
100
+ image_byte = pil2byte(pil_image)
101
+ files = {"image": ("output.png", image_byte, 'image/png')}
102
+ data['strength'] = strength
103
+
104
+ response = requests.post(url,
105
+ headers={"authorization": f"{api_key}", "accept": "application/json"},
106
+ files=files,
107
+ data=data,
108
+ )
109
+ if response.status_code == 200:
110
+ PromptServer.instance.send_sync('stable-diffusion-api-generate-succeed',{"model":model})
111
+ json_data = response.json()
112
+ image_base64 = json_data['image']
113
+ image_data = image2base64(image_base64)
114
+ output_t = pil2tensor(image_data)
115
+ return output_t
116
+ else:
117
+ if 'application/json' in response.headers['Content-Type']:
118
+ error_info = response.json()
119
+ log_node_error(node_name, error_info.get('name', 'No name provided'))
120
+ log_node_error(node_name, error_info.get('errors', ['No details provided']))
121
+ error_status_text = self.getErrors(response.status_code)
122
+ PromptServer.instance.send_sync('easyuse-toast',{"type": "error", "content": error_status_text})
123
+ raise Exception(f"Failed to generate image: {error_status_text}")
124
+
125
+ # get user account
126
+ async def getUserAccount(self, cache=True):
127
+ url = f"{self.api_url}/v1/user/account"
128
+ api_key = self.api_keys[self.api_current]['key']
129
+ name = self.api_keys[self.api_current]['name']
130
+ if cache and name in self.user_info:
131
+ return self.user_info[name]
132
+ else:
133
+ response = requests.get(url, headers={"Authorization": f"Bearer {api_key}"})
134
+ if response.status_code == 200:
135
+ user_info = response.json()
136
+ self.user_info[name] = user_info
137
+ return user_info
138
+ else:
139
+ PromptServer.instance.send_sync('easyuse-toast',{'type': 'error', 'content': self.getErrors(response.status_code)})
140
+ return None
141
+
142
+ # get user balance
143
+ async def getUserBalance(self):
144
+ url = f"{self.api_url}/v1/user/balance"
145
+ api_key = self.api_keys[self.api_current]['key']
146
+ response = requests.get(url, headers={
147
+ "Authorization": f"Bearer {api_key}"
148
+ })
149
+ if response.status_code == 200:
150
+ return response.json()
151
+ else:
152
+ PromptServer.instance.send_sync('easyuse-toast', {'type': 'error', 'content': self.getErrors(response.status_code)})
153
+ return None
154
+
155
+ stableAPI = StabilityAPI()
156
+
157
+ @PromptServer.instance.routes.get("/easyuse/stability/api_keys")
158
+ async def get_stability_api_keys(request):
159
+ stableAPI.getAPIKeys()
160
+ return web.json_response({"keys": stableAPI.api_keys, "current": stableAPI.api_current})
161
+
162
+ @PromptServer.instance.routes.post("/easyuse/stability/set_api_keys")
163
+ async def set_stability_api_keys(request):
164
+ post = await request.post()
165
+ api_keys = post.get("api_keys")
166
+ current = post.get('current')
167
+ if api_keys is not None:
168
+ api_keys = json.loads(api_keys)
169
+ stableAPI.setAPIKeys(api_keys)
170
+ if current is not None:
171
+ print(current)
172
+ stableAPI.setAPIDefault(int(current))
173
+ account = await stableAPI.getUserAccount()
174
+ balance = await stableAPI.getUserBalance()
175
+ return web.json_response({'account': account, 'balance': balance})
176
+ else:
177
+ return web.json_response({'status': 'ok'})
178
+ else:
179
+ return web.Response(status=400)
180
+
181
+ @PromptServer.instance.routes.post("/easyuse/stability/set_apikey_default")
182
+ async def set_stability_api_default(request):
183
+ post = await request.post()
184
+ current = post.get("current")
185
+ if current is not None and current < len(stableAPI.api_keys):
186
+ stableAPI.api_current = current
187
+ return web.json_response({'status': 'ok'})
188
+ else:
189
+ return web.Response(status=400)
190
+
191
+ @PromptServer.instance.routes.get("/easyuse/stability/user_info")
192
+ async def get_account_info(request):
193
+ account = await stableAPI.getUserAccount()
194
+ balance = await stableAPI.getUserBalance()
195
+ return web.json_response({'account': account, 'balance': balance})
196
+
197
+ @PromptServer.instance.routes.get("/easyuse/stability/balance")
198
+ async def get_balance_info(request):
199
+ balance = await stableAPI.getUserBalance()
200
+ return web.json_response({'balance': balance})
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/cache.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import itertools
2
+ from typing import Optional
3
+
4
+ class TaggedCache:
5
+ def __init__(self, tag_settings: Optional[dict]=None):
6
+ self._tag_settings = tag_settings or {} # tag cache size
7
+ self._data = {}
8
+
9
+ def __getitem__(self, key):
10
+ for tag_data in self._data.values():
11
+ if key in tag_data:
12
+ return tag_data[key]
13
+ raise KeyError(f'Key `{key}` does not exist')
14
+
15
+ def __setitem__(self, key, value: tuple):
16
+ # value: (tag: str, (islist: bool, data: *))
17
+
18
+ # if key already exists, pop old value
19
+ for tag_data in self._data.values():
20
+ if key in tag_data:
21
+ tag_data.pop(key, None)
22
+ break
23
+
24
+ tag = value[0]
25
+ if tag not in self._data:
26
+
27
+ try:
28
+ from cachetools import LRUCache
29
+
30
+ default_size = 20
31
+ if 'ckpt' in tag:
32
+ default_size = 5
33
+ elif tag in ['latent', 'image']:
34
+ default_size = 100
35
+
36
+ self._data[tag] = LRUCache(maxsize=self._tag_settings.get(tag, default_size))
37
+
38
+ except (ImportError, ModuleNotFoundError):
39
+ # TODO: implement a simple lru dict
40
+ self._data[tag] = {}
41
+ self._data[tag][key] = value
42
+
43
+ def __delitem__(self, key):
44
+ for tag_data in self._data.values():
45
+ if key in tag_data:
46
+ del tag_data[key]
47
+ return
48
+ raise KeyError(f'Key `{key}` does not exist')
49
+
50
+ def __contains__(self, key):
51
+ return any(key in tag_data for tag_data in self._data.values())
52
+
53
+ def items(self):
54
+ yield from itertools.chain(*map(lambda x :x.items(), self._data.values()))
55
+
56
+ def get(self, key, default=None):
57
+ """D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."""
58
+ for tag_data in self._data.values():
59
+ if key in tag_data:
60
+ return tag_data[key]
61
+ return default
62
+
63
+ def clear(self):
64
+ # clear all cache
65
+ self._data = {}
66
+
67
+ cache_settings = {}
68
+ cache = TaggedCache(cache_settings)
69
+ cache_count = {}
70
+
71
+ def update_cache(k, tag, v):
72
+ cache[k] = (tag, v)
73
+ cnt = cache_count.get(k)
74
+ if cnt is None:
75
+ cnt = 0
76
+ cache_count[k] = cnt
77
+ else:
78
+ cache_count[k] += 1
79
+ def remove_cache(key):
80
+ if key == '*':
81
+ cache.clear()
82
+ cache_count.clear()
83
+ elif key in cache:
84
+ del cache[key]
85
+ cache_count.pop(key, None)
86
+ else:
87
+ print(f"invalid {key}")
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/chooser.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from threading import Event
2
+
3
+ import torch
4
+
5
+ from server import PromptServer
6
+ from aiohttp import web
7
+ from comfy import model_management as mm
8
+ from comfy_execution.graph import ExecutionBlocker
9
+ import time
10
+
11
+ class ChooserCancelled(Exception):
12
+ pass
13
+
14
+ def get_chooser_cache():
15
+ """获取选择器缓存"""
16
+ if not hasattr(PromptServer.instance, '_easyuse_chooser_node'):
17
+ PromptServer.instance._easyuse_chooser_node = {}
18
+ return PromptServer.instance._easyuse_chooser_node
19
+
20
+ def cleanup_session_data(node_id):
21
+ """清理会话数据"""
22
+ node_data = get_chooser_cache()
23
+ if node_id in node_data:
24
+ session_keys = ["event", "selected", "images", "total_count", "cancelled"]
25
+ for key in session_keys:
26
+ if key in node_data[node_id]:
27
+ del node_data[node_id][key]
28
+
29
+ def wait_for_chooser(id, images, mode, period=0.1):
30
+ try:
31
+ node_data = get_chooser_cache()
32
+ images = [images[i:i + 1, ...] for i in range(images.shape[0])]
33
+ if mode == "Keep Last Selection":
34
+ if id in node_data and "last_selection" in node_data[id]:
35
+ last_selection = node_data[id]["last_selection"]
36
+ if last_selection and len(last_selection) > 0:
37
+ valid_indices = [idx for idx in last_selection if 0 <= idx < len(images)]
38
+ if valid_indices:
39
+ try:
40
+ PromptServer.instance.send_sync("easyuse-image-keep-selection", {
41
+ "id": id,
42
+ "selected": valid_indices
43
+ })
44
+ except Exception as e:
45
+ pass
46
+ cleanup_session_data(id)
47
+ indices_str = ','.join(str(i) for i in valid_indices)
48
+ images = [images[idx] for idx in valid_indices]
49
+ images = torch.cat(images, dim=0)
50
+ return {"result": (images,)}
51
+
52
+ if id in node_data:
53
+ del node_data[id]
54
+
55
+ event = Event()
56
+ node_data[id] = {
57
+ "event": event,
58
+ "images": images,
59
+ "selected": None,
60
+ "total_count": len(images),
61
+ "cancelled": False,
62
+ }
63
+
64
+ while id in node_data:
65
+ node_info = node_data[id]
66
+ if node_info.get("cancelled", False):
67
+ cleanup_session_data(id)
68
+ raise ChooserCancelled("Manual selection cancelled")
69
+
70
+ if "selected" in node_info and node_info["selected"] is not None:
71
+ break
72
+
73
+ time.sleep(period)
74
+
75
+ if id in node_data:
76
+ node_info = node_data[id]
77
+ selected_indices = node_info.get("selected")
78
+
79
+ if selected_indices is not None and len(selected_indices) > 0:
80
+ valid_indices = [idx for idx in selected_indices if 0 <= idx < len(images)]
81
+ if valid_indices:
82
+ selected_images = [images[idx] for idx in valid_indices]
83
+
84
+ if id not in node_data:
85
+ node_data[id] = {}
86
+ node_data[id]["last_selection"] = valid_indices
87
+ cleanup_session_data(id)
88
+ selected_images = torch.cat(selected_images, dim=0)
89
+ return {"result": (selected_images,)}
90
+ else:
91
+ cleanup_session_data(id)
92
+ return {"result": (images[0] if len(images) > 0 else ExecutionBlocker(None),)}
93
+ else:
94
+ cleanup_session_data(id)
95
+ return {
96
+ "result": (images[0] if len(images) > 0 else ExecutionBlocker(None),)}
97
+ else:
98
+ return {"result": (images[0] if len(images) > 0 else ExecutionBlocker(None),)}
99
+
100
+ except ChooserCancelled:
101
+ raise mm.InterruptProcessingException()
102
+ except Exception as e:
103
+ node_data = get_chooser_cache()
104
+ if id in node_data:
105
+ cleanup_session_data(id)
106
+ if 'image_list' in locals() and len(images) > 0:
107
+ return {"result": (images[0])}
108
+ else:
109
+ return {"result": (ExecutionBlocker(None),)}
110
+
111
+
112
+ @PromptServer.instance.routes.post('/easyuse/image_chooser_message')
113
+ async def handle_image_selection(request):
114
+ try:
115
+ data = await request.json()
116
+ node_id = data.get("node_id")
117
+ selected = data.get("selected", [])
118
+ action = data.get("action")
119
+
120
+ node_data = get_chooser_cache()
121
+
122
+ if node_id not in node_data:
123
+ return web.json_response({"code": -1, "error": "Node data does not exist"})
124
+
125
+ try:
126
+ node_info = node_data[node_id]
127
+
128
+ if "total_count" not in node_info:
129
+ return web.json_response({"code": -1, "error": "The node has been processed"})
130
+
131
+ if action == "cancel":
132
+ node_info["cancelled"] = True
133
+ node_info["selected"] = []
134
+ elif action == "select" and isinstance(selected, list):
135
+ valid_indices = [idx for idx in selected if isinstance(idx, int) and 0 <= idx < node_info["total_count"]]
136
+ if valid_indices:
137
+ node_info["selected"] = valid_indices
138
+ node_info["cancelled"] = False
139
+ else:
140
+ return web.json_response({"code": -1, "error": "Invalid Selection Index"})
141
+ else:
142
+ return web.json_response({"code": -1, "error": "Invalid operation"})
143
+
144
+ node_info["event"].set()
145
+ return web.json_response({"code": 1})
146
+
147
+ except Exception as e:
148
+ if node_id in node_data and "event" in node_data[node_id]:
149
+ node_data[node_id]["event"].set()
150
+ return web.json_response({"code": -1, "message": "Processing Failed"})
151
+
152
+ except Exception as e:
153
+ return web.json_response({"code": -1, "message": "Request Failed"})
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/colorfix.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from PIL import Image
3
+ from torch import Tensor
4
+ from torch.nn import functional as F
5
+
6
+ from torchvision.transforms import ToTensor, ToPILImage
7
+
8
+ def adain_color_fix(target: Image, source: Image):
9
+ # Convert images to tensors
10
+ to_tensor = ToTensor()
11
+ target_tensor = to_tensor(target).unsqueeze(0)
12
+ source_tensor = to_tensor(source).unsqueeze(0)
13
+
14
+ # Apply adaptive instance normalization
15
+ result_tensor = adaptive_instance_normalization(target_tensor, source_tensor)
16
+
17
+ # Convert tensor back to image
18
+ to_image = ToPILImage()
19
+ result_image = to_image(result_tensor.squeeze(0).clamp_(0.0, 1.0))
20
+
21
+ return result_image
22
+
23
+ def wavelet_color_fix(target: Image, source: Image):
24
+ source = source.resize(target.size, resample=Image.Resampling.LANCZOS)
25
+
26
+ # Convert images to tensors
27
+ to_tensor = ToTensor()
28
+ target_tensor = to_tensor(target).unsqueeze(0)
29
+ source_tensor = to_tensor(source).unsqueeze(0)
30
+
31
+ # Apply wavelet reconstruction
32
+ result_tensor = wavelet_reconstruction(target_tensor, source_tensor)
33
+
34
+ # Convert tensor back to image
35
+ to_image = ToPILImage()
36
+ result_image = to_image(result_tensor.squeeze(0).clamp_(0.0, 1.0))
37
+
38
+ return result_image
39
+
40
+ def calc_mean_std(feat: Tensor, eps=1e-5):
41
+ """Calculate mean and std for adaptive_instance_normalization.
42
+ Args:
43
+ feat (Tensor): 4D tensor.
44
+ eps (float): A small value added to the variance to avoid
45
+ divide-by-zero. Default: 1e-5.
46
+ """
47
+ size = feat.size()
48
+ assert len(size) == 4, 'The input feature should be 4D tensor.'
49
+ b, c = size[:2]
50
+ feat_var = feat.view(b, c, -1).var(dim=2) + eps
51
+ feat_std = feat_var.sqrt().view(b, c, 1, 1)
52
+ feat_mean = feat.view(b, c, -1).mean(dim=2).view(b, c, 1, 1)
53
+ return feat_mean, feat_std
54
+
55
+ def adaptive_instance_normalization(content_feat:Tensor, style_feat:Tensor):
56
+ """Adaptive instance normalization.
57
+ Adjust the reference features to have the similar color and illuminations
58
+ as those in the degradate features.
59
+ Args:
60
+ content_feat (Tensor): The reference feature.
61
+ style_feat (Tensor): The degradate features.
62
+ """
63
+ size = content_feat.size()
64
+ style_mean, style_std = calc_mean_std(style_feat)
65
+ content_mean, content_std = calc_mean_std(content_feat)
66
+ normalized_feat = (content_feat - content_mean.expand(size)) / content_std.expand(size)
67
+ return normalized_feat * style_std.expand(size) + style_mean.expand(size)
68
+
69
+ def wavelet_blur(image: Tensor, radius: int):
70
+ """
71
+ Apply wavelet blur to the input tensor.
72
+ """
73
+ # input shape: (1, 3, H, W)
74
+ # convolution kernel
75
+ kernel_vals = [
76
+ [0.0625, 0.125, 0.0625],
77
+ [0.125, 0.25, 0.125],
78
+ [0.0625, 0.125, 0.0625],
79
+ ]
80
+ kernel = torch.tensor(kernel_vals, dtype=image.dtype, device=image.device)
81
+ # add channel dimensions to the kernel to make it a 4D tensor
82
+ kernel = kernel[None, None]
83
+ # repeat the kernel across all input channels
84
+ kernel = kernel.repeat(3, 1, 1, 1)
85
+ image = F.pad(image, (radius, radius, radius, radius), mode='replicate')
86
+ # apply convolution
87
+ output = F.conv2d(image, kernel, groups=3, dilation=radius)
88
+ return output
89
+
90
+ def wavelet_decomposition(image: Tensor, levels=5):
91
+ """
92
+ Apply wavelet decomposition to the input tensor.
93
+ This function only returns the low frequency & the high frequency.
94
+ """
95
+ high_freq = torch.zeros_like(image)
96
+ for i in range(levels):
97
+ radius = 2 ** i
98
+ low_freq = wavelet_blur(image, radius)
99
+ high_freq += (image - low_freq)
100
+ image = low_freq
101
+
102
+ return high_freq, low_freq
103
+
104
+ def wavelet_reconstruction(content_feat:Tensor, style_feat:Tensor):
105
+ """
106
+ Apply wavelet decomposition, so that the content will have the same color as the style.
107
+ """
108
+ # calculate the wavelet decomposition of the content feature
109
+ content_high_freq, content_low_freq = wavelet_decomposition(content_feat)
110
+ del content_low_freq
111
+ # calculate the wavelet decomposition of the style feature
112
+ style_high_freq, style_low_freq = wavelet_decomposition(style_feat)
113
+ del style_high_freq
114
+ # reconstruct the content feature with the style's high frequency
115
+ return content_high_freq + style_low_freq
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/conditioning.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .utils import find_wildcards_seed, find_nearest_steps, is_linked_styles_selector
2
+ from .log import log_node_warn
3
+ from .translate import zh_to_en, has_chinese
4
+ from .wildcards import process_with_loras
5
+ from .adv_encode import advanced_encode
6
+
7
+ from nodes import ConditioningConcat, ConditioningCombine, ConditioningAverage, ConditioningSetTimestepRange, CLIPTextEncode
8
+
9
+ def prompt_to_cond(type, model, clip, clip_skip, lora_stack, text, prompt_token_normalization, prompt_weight_interpretation, a1111_prompt_style ,my_unique_id, prompt, easyCache, can_load_lora=True, steps=None, model_type=None):
10
+ styles_selector = is_linked_styles_selector(prompt, my_unique_id, type)
11
+ title = "Positive encoding" if type == 'positive' else "Negative encoding"
12
+
13
+ # Translate cn to en
14
+ if model_type not in ['hydit'] and text is not None and has_chinese(text):
15
+ text = zh_to_en([text])[0]
16
+
17
+ if model_type in ['hydit', 'flux', 'mochi']:
18
+ log_node_warn(title + "...")
19
+ embeddings_final, = CLIPTextEncode().encode(clip, text) if text is not None else (None,)
20
+
21
+ return (embeddings_final, "", model, clip)
22
+
23
+ log_node_warn(title + "...")
24
+
25
+ positive_seed = find_wildcards_seed(my_unique_id, text, prompt)
26
+ model, clip, text, cond_decode, show_prompt, pipe_lora_stack = process_with_loras(
27
+ text, model, clip, type, positive_seed, can_load_lora, lora_stack, easyCache)
28
+ wildcard_prompt = cond_decode if show_prompt or styles_selector else ""
29
+
30
+ clipped = clip.clone()
31
+ # 当clip模型不存在t5xxl时,可执行跳过层
32
+ if not hasattr(clip.cond_stage_model, 't5xxl'):
33
+ if clip_skip != 0:
34
+ clipped.clip_layer(clip_skip)
35
+
36
+ steps = steps if steps is not None else find_nearest_steps(my_unique_id, prompt)
37
+ return (advanced_encode(clipped, text, prompt_token_normalization,
38
+ prompt_weight_interpretation, w_max=1.0,
39
+ apply_to_pooled='enable',
40
+ a1111_prompt_style=a1111_prompt_style, steps=steps) if text is not None else None, wildcard_prompt, model, clipped)
41
+
42
+ def set_cond(old_cond, new_cond, mode, average_strength, old_cond_start, old_cond_end, new_cond_start, new_cond_end):
43
+ if not old_cond:
44
+ return new_cond
45
+ else:
46
+ if mode == "replace":
47
+ return new_cond
48
+ elif mode == "concat":
49
+ return ConditioningConcat().concat(new_cond, old_cond)[0]
50
+ elif mode == "combine":
51
+ return ConditioningCombine().combine(old_cond, new_cond)[0]
52
+ elif mode == 'average':
53
+ return ConditioningAverage().addWeighted(new_cond, old_cond, average_strength)[0]
54
+ elif mode == 'timestep':
55
+ cond_1 = ConditioningSetTimestepRange().set_range(old_cond, old_cond_start, old_cond_end)[0]
56
+ cond_2 = ConditioningSetTimestepRange().set_range(new_cond, new_cond_start, new_cond_end)[0]
57
+ return ConditioningCombine().combine(cond_1, cond_2)[0]
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/controlnet.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import folder_paths
2
+ import comfy.controlnet
3
+ import comfy.model_management
4
+ from nodes import NODE_CLASS_MAPPINGS
5
+
6
+ union_controlnet_types = {"auto": -1, "openpose": 0, "depth": 1, "hed/pidi/scribble/ted": 2, "canny/lineart/anime_lineart/mlsd": 3, "normal": 4, "segment": 5, "tile": 6, "repaint": 7}
7
+
8
+ class easyControlnet:
9
+ def __init__(self):
10
+ pass
11
+
12
+ def apply(self, control_net_name, image, positive, negative, strength, start_percent=0, end_percent=1, control_net=None, scale_soft_weights=1, mask=None, union_type=None, easyCache=None, use_cache=True, model=None, vae=None):
13
+ if strength == 0:
14
+ return (positive, negative)
15
+
16
+ # kolors controlnet patch
17
+ from ..modules.kolors.loader import is_kolors_model, applyKolorsUnet
18
+ if is_kolors_model(model):
19
+ from ..modules.kolors.model_patch import patch_controlnet
20
+ if control_net is None:
21
+ with applyKolorsUnet():
22
+ control_net = easyCache.load_controlnet(control_net_name, scale_soft_weights, use_cache)
23
+ control_net = patch_controlnet(model, control_net)
24
+ else:
25
+ if control_net is None:
26
+ if easyCache is not None:
27
+ control_net = easyCache.load_controlnet(control_net_name, scale_soft_weights, use_cache)
28
+ else:
29
+ controlnet_path = folder_paths.get_full_path("controlnet", control_net_name)
30
+ control_net = comfy.controlnet.load_controlnet(controlnet_path)
31
+
32
+ # union controlnet
33
+ if union_type is not None:
34
+ control_net = control_net.copy()
35
+ type_number = union_controlnet_types[union_type]
36
+ if type_number >= 0:
37
+ control_net.set_extra_arg("control_type", [type_number])
38
+ else:
39
+ control_net.set_extra_arg("control_type", [])
40
+
41
+ if mask is not None:
42
+ mask = mask.to(self.device)
43
+
44
+ if mask is not None and len(mask.shape) < 3:
45
+ mask = mask.unsqueeze(0)
46
+
47
+ control_hint = image.movedim(-1, 1)
48
+
49
+ is_cond = True
50
+ if negative is None:
51
+ p = []
52
+ for t in positive:
53
+ n = [t[0], t[1].copy()]
54
+ c_net = control_net.copy().set_cond_hint(control_hint, strength, (start_percent, end_percent))
55
+ if 'control' in t[1]:
56
+ c_net.set_previous_controlnet(t[1]['control'])
57
+ n[1]['control'] = c_net
58
+ n[1]['control_apply_to_uncond'] = True
59
+ if mask is not None:
60
+ n[1]['mask'] = mask
61
+ n[1]['set_area_to_bounds'] = False
62
+ p.append(n)
63
+ positive = p
64
+ else:
65
+ cnets = {}
66
+ out = []
67
+ for conditioning in [positive, negative]:
68
+ c = []
69
+ for t in conditioning:
70
+ d = t[1].copy()
71
+
72
+ prev_cnet = d.get('control', None)
73
+ if prev_cnet in cnets:
74
+ c_net = cnets[prev_cnet]
75
+ else:
76
+ c_net = control_net.copy().set_cond_hint(control_hint, strength, (start_percent, end_percent), vae)
77
+ c_net.set_previous_controlnet(prev_cnet)
78
+ cnets[prev_cnet] = c_net
79
+
80
+ d['control'] = c_net
81
+ d['control_apply_to_uncond'] = False
82
+
83
+ if mask is not None:
84
+ d['mask'] = mask
85
+ d['set_area_to_bounds'] = False
86
+
87
+ n = [t[0], d]
88
+ c.append(n)
89
+ out.append(c)
90
+ positive = out[0]
91
+ negative = out[1]
92
+
93
+ return (positive, negative)
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/dynthres_core.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch, math
2
+
3
+ ######################### DynThresh Core #########################
4
+
5
+ class DynThresh:
6
+
7
+ Modes = ["Constant", "Linear Down", "Cosine Down", "Half Cosine Down", "Linear Up", "Cosine Up", "Half Cosine Up", "Power Up", "Power Down", "Linear Repeating", "Cosine Repeating", "Sawtooth"]
8
+ Startpoints = ["MEAN", "ZERO"]
9
+ Variabilities = ["AD", "STD"]
10
+
11
+ def __init__(self, mimic_scale, threshold_percentile, mimic_mode, mimic_scale_min, cfg_mode, cfg_scale_min, sched_val, experiment_mode, max_steps, separate_feature_channels, scaling_startpoint, variability_measure, interpolate_phi):
12
+ self.mimic_scale = mimic_scale
13
+ self.threshold_percentile = threshold_percentile
14
+ self.mimic_mode = mimic_mode
15
+ self.cfg_mode = cfg_mode
16
+ self.max_steps = max_steps
17
+ self.cfg_scale_min = cfg_scale_min
18
+ self.mimic_scale_min = mimic_scale_min
19
+ self.experiment_mode = experiment_mode
20
+ self.sched_val = sched_val
21
+ self.sep_feat_channels = separate_feature_channels
22
+ self.scaling_startpoint = scaling_startpoint
23
+ self.variability_measure = variability_measure
24
+ self.interpolate_phi = interpolate_phi
25
+
26
+ def interpret_scale(self, scale, mode, min):
27
+ scale -= min
28
+ max = self.max_steps - 1
29
+ frac = self.step / max
30
+ if mode == "Constant":
31
+ pass
32
+ elif mode == "Linear Down":
33
+ scale *= 1.0 - frac
34
+ elif mode == "Half Cosine Down":
35
+ scale *= math.cos(frac)
36
+ elif mode == "Cosine Down":
37
+ scale *= math.cos(frac * 1.5707)
38
+ elif mode == "Linear Up":
39
+ scale *= frac
40
+ elif mode == "Half Cosine Up":
41
+ scale *= 1.0 - math.cos(frac)
42
+ elif mode == "Cosine Up":
43
+ scale *= 1.0 - math.cos(frac * 1.5707)
44
+ elif mode == "Power Up":
45
+ scale *= math.pow(frac, self.sched_val)
46
+ elif mode == "Power Down":
47
+ scale *= 1.0 - math.pow(frac, self.sched_val)
48
+ elif mode == "Linear Repeating":
49
+ portion = (frac * self.sched_val) % 1.0
50
+ scale *= (0.5 - portion) * 2 if portion < 0.5 else (portion - 0.5) * 2
51
+ elif mode == "Cosine Repeating":
52
+ scale *= math.cos(frac * 6.28318 * self.sched_val) * 0.5 + 0.5
53
+ elif mode == "Sawtooth":
54
+ scale *= (frac * self.sched_val) % 1.0
55
+ scale += min
56
+ return scale
57
+
58
+ def dynthresh(self, cond, uncond, cfg_scale, weights):
59
+ mimic_scale = self.interpret_scale(self.mimic_scale, self.mimic_mode, self.mimic_scale_min)
60
+ cfg_scale = self.interpret_scale(cfg_scale, self.cfg_mode, self.cfg_scale_min)
61
+ # uncond shape is (batch, 4, height, width)
62
+ conds_per_batch = cond.shape[0] / uncond.shape[0]
63
+ assert conds_per_batch == int(conds_per_batch), "Expected # of conds per batch to be constant across batches"
64
+ cond_stacked = cond.reshape((-1, int(conds_per_batch)) + uncond.shape[1:])
65
+
66
+ ### Normal first part of the CFG Scale logic, basically
67
+ diff = cond_stacked - uncond.unsqueeze(1)
68
+ if weights is not None:
69
+ diff = diff * weights
70
+ relative = diff.sum(1)
71
+
72
+ ### Get the normal result for both mimic and normal scale
73
+ mim_target = uncond + relative * mimic_scale
74
+ cfg_target = uncond + relative * cfg_scale
75
+ ### If we weren't doing mimic scale, we'd just return cfg_target here
76
+
77
+ ### Now recenter the values relative to their average rather than absolute, to allow scaling from average
78
+ mim_flattened = mim_target.flatten(2)
79
+ cfg_flattened = cfg_target.flatten(2)
80
+ mim_means = mim_flattened.mean(dim=2).unsqueeze(2)
81
+ cfg_means = cfg_flattened.mean(dim=2).unsqueeze(2)
82
+ mim_centered = mim_flattened - mim_means
83
+ cfg_centered = cfg_flattened - cfg_means
84
+
85
+ if self.sep_feat_channels:
86
+ if self.variability_measure == 'STD':
87
+ mim_scaleref = mim_centered.std(dim=2).unsqueeze(2)
88
+ cfg_scaleref = cfg_centered.std(dim=2).unsqueeze(2)
89
+ else: # 'AD'
90
+ mim_scaleref = mim_centered.abs().max(dim=2).values.unsqueeze(2)
91
+ cfg_scaleref = torch.quantile(cfg_centered.abs(), self.threshold_percentile, dim=2).unsqueeze(2)
92
+
93
+ else:
94
+ if self.variability_measure == 'STD':
95
+ mim_scaleref = mim_centered.std()
96
+ cfg_scaleref = cfg_centered.std()
97
+ else: # 'AD'
98
+ mim_scaleref = mim_centered.abs().max()
99
+ cfg_scaleref = torch.quantile(cfg_centered.abs(), self.threshold_percentile)
100
+
101
+ if self.scaling_startpoint == 'ZERO':
102
+ scaling_factor = mim_scaleref / cfg_scaleref
103
+ result = cfg_flattened * scaling_factor
104
+
105
+ else: # 'MEAN'
106
+ if self.variability_measure == 'STD':
107
+ cfg_renormalized = (cfg_centered / cfg_scaleref) * mim_scaleref
108
+ else: # 'AD'
109
+ ### Get the maximum value of all datapoints (with an optional threshold percentile on the uncond)
110
+ max_scaleref = torch.maximum(mim_scaleref, cfg_scaleref)
111
+ ### Clamp to the max
112
+ cfg_clamped = cfg_centered.clamp(-max_scaleref, max_scaleref)
113
+ ### Now shrink from the max to normalize and grow to the mimic scale (instead of the CFG scale)
114
+ cfg_renormalized = (cfg_clamped / max_scaleref) * mim_scaleref
115
+
116
+ ### Now add it back onto the averages to get into real scale again and return
117
+ result = cfg_renormalized + cfg_means
118
+
119
+ actual_res = result.unflatten(2, mim_target.shape[2:])
120
+
121
+ if self.interpolate_phi != 1.0:
122
+ actual_res = actual_res * self.interpolate_phi + cfg_target * (1.0 - self.interpolate_phi)
123
+
124
+ if self.experiment_mode == 1:
125
+ num = actual_res.cpu().numpy()
126
+ for y in range(0, 64):
127
+ for x in range (0, 64):
128
+ if num[0][0][y][x] > 1.0:
129
+ num[0][1][y][x] *= 0.5
130
+ if num[0][1][y][x] > 1.0:
131
+ num[0][1][y][x] *= 0.5
132
+ if num[0][2][y][x] > 1.5:
133
+ num[0][2][y][x] *= 0.5
134
+ actual_res = torch.from_numpy(num).to(device=uncond.device)
135
+ elif self.experiment_mode == 2:
136
+ num = actual_res.cpu().numpy()
137
+ for y in range(0, 64):
138
+ for x in range (0, 64):
139
+ over_scale = False
140
+ for z in range(0, 4):
141
+ if abs(num[0][z][y][x]) > 1.5:
142
+ over_scale = True
143
+ if over_scale:
144
+ for z in range(0, 4):
145
+ num[0][z][y][x] *= 0.7
146
+ actual_res = torch.from_numpy(num).to(device=uncond.device)
147
+ elif self.experiment_mode == 3:
148
+ coefs = torch.tensor([
149
+ # R G B W
150
+ [0.298, 0.207, 0.208, 0.0], # L1
151
+ [0.187, 0.286, 0.173, 0.0], # L2
152
+ [-0.158, 0.189, 0.264, 0.0], # L3
153
+ [-0.184, -0.271, -0.473, 1.0], # L4
154
+ ], device=uncond.device)
155
+ res_rgb = torch.einsum("laxy,ab -> lbxy", actual_res, coefs)
156
+ max_r, max_g, max_b, max_w = res_rgb[0][0].max(), res_rgb[0][1].max(), res_rgb[0][2].max(), res_rgb[0][3].max()
157
+ max_rgb = max(max_r, max_g, max_b)
158
+ print(f"test max = r={max_r}, g={max_g}, b={max_b}, w={max_w}, rgb={max_rgb}")
159
+ if self.step / (self.max_steps - 1) > 0.2:
160
+ if max_rgb < 2.0 and max_w < 3.0:
161
+ res_rgb /= max_rgb / 2.4
162
+ else:
163
+ if max_rgb > 2.4 and max_w > 3.0:
164
+ res_rgb /= max_rgb / 2.4
165
+ actual_res = torch.einsum("laxy,ab -> lbxy", res_rgb, coefs.inverse())
166
+
167
+ return actual_res
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/easing.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @staticmethod
2
+ def easyIn(t: float)-> float:
3
+ return t*t
4
+ @staticmethod
5
+ def easyOut(t: float)-> float:
6
+ return -(t * (t - 2))
7
+ @staticmethod
8
+ def easyInOut(t: float)-> float:
9
+ if t < 0.5:
10
+ return 2*t*t
11
+ else:
12
+ return (-2*t*t) + (4*t) - 1
13
+
14
+ class EasingBase:
15
+
16
+ def easing(self, t: float, function='linear') -> float:
17
+ if function == 'easyIn':
18
+ return easyIn(t)
19
+ elif function == 'easyOut':
20
+ return easyOut(t)
21
+ elif function == 'easyInOut':
22
+ return easyInOut(t)
23
+ else:
24
+ return t
25
+
26
+ def ease(self, start, end, t) -> float:
27
+ return end * t + start * (1 - t)
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/gradual_latent_hires_fix.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torchvision.transforms.functional import gaussian_blur
3
+ from comfy.k_diffusion.sampling import default_noise_sampler, get_ancestral_step, to_d, BrownianTreeNoiseSampler
4
+ from tqdm.auto import trange
5
+
6
+ @torch.no_grad()
7
+ def sample_euler_ancestral(
8
+ model,
9
+ x,
10
+ sigmas,
11
+ extra_args=None,
12
+ callback=None,
13
+ disable=None,
14
+ eta=1.0,
15
+ s_noise=1.0,
16
+ noise_sampler=None,
17
+ upscale_ratio=2.0,
18
+ start_step=5,
19
+ end_step=15,
20
+ upscale_n_step=3,
21
+ unsharp_kernel_size=3,
22
+ unsharp_sigma=0.5,
23
+ unsharp_strength=0.0,
24
+ ):
25
+ """Ancestral sampling with Euler method steps."""
26
+ extra_args = {} if extra_args is None else extra_args
27
+ noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler
28
+ s_in = x.new_ones([x.shape[0]])
29
+
30
+ # make upscale info
31
+ upscale_steps = []
32
+ step = start_step - 1
33
+ while step < end_step - 1:
34
+ upscale_steps.append(step)
35
+ step += upscale_n_step
36
+ height, width = x.shape[2:]
37
+ upscale_shapes = [
38
+ (int(height * (((upscale_ratio - 1) / i) + 1)), int(width * (((upscale_ratio - 1) / i) + 1)))
39
+ for i in reversed(range(1, len(upscale_steps) + 1))
40
+ ]
41
+ upscale_info = {k: v for k, v in zip(upscale_steps, upscale_shapes)}
42
+
43
+ for i in trange(len(sigmas) - 1, disable=disable):
44
+ denoised = model(x, sigmas[i] * s_in, **extra_args)
45
+ sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1], eta=eta)
46
+ if callback is not None:
47
+ callback({"x": x, "i": i, "sigma": sigmas[i], "sigma_hat": sigmas[i], "denoised": denoised})
48
+ d = to_d(x, sigmas[i], denoised)
49
+ # Euler method
50
+ dt = sigma_down - sigmas[i]
51
+ x = x + d * dt
52
+ if sigmas[i + 1] > 0:
53
+ # Resize
54
+ if i in upscale_info:
55
+ x = torch.nn.functional.interpolate(x, size=upscale_info[i], mode="bicubic", align_corners=False)
56
+ if unsharp_strength > 0:
57
+ blurred = gaussian_blur(x, kernel_size=unsharp_kernel_size, sigma=unsharp_sigma)
58
+ x = x + unsharp_strength * (x - blurred)
59
+
60
+ noise_sampler = default_noise_sampler(x)
61
+ noise = noise_sampler(sigmas[i], sigmas[i + 1])
62
+ x = x + noise * sigma_up * s_noise
63
+ return x
64
+
65
+
66
+ @torch.no_grad()
67
+ def sample_dpmpp_2s_ancestral(
68
+ model,
69
+ x,
70
+ sigmas,
71
+ extra_args=None,
72
+ callback=None,
73
+ disable=None,
74
+ eta=1.0,
75
+ s_noise=1.0,
76
+ noise_sampler=None,
77
+ upscale_ratio=2.0,
78
+ start_step=5,
79
+ end_step=15,
80
+ upscale_n_step=3,
81
+ unsharp_kernel_size=3,
82
+ unsharp_sigma=0.5,
83
+ unsharp_strength=0.0,
84
+ ):
85
+ """Ancestral sampling with DPM-Solver++(2S) second-order steps."""
86
+ extra_args = {} if extra_args is None else extra_args
87
+ s_in = x.new_ones([x.shape[0]])
88
+ sigma_fn = lambda t: t.neg().exp()
89
+ t_fn = lambda sigma: sigma.log().neg()
90
+
91
+ # make upscale info
92
+ upscale_steps = []
93
+ step = start_step - 1
94
+ while step < end_step - 1:
95
+ upscale_steps.append(step)
96
+ step += upscale_n_step
97
+ height, width = x.shape[2:]
98
+ upscale_shapes = [
99
+ (int(height * (((upscale_ratio - 1) / i) + 1)), int(width * (((upscale_ratio - 1) / i) + 1)))
100
+ for i in reversed(range(1, len(upscale_steps) + 1))
101
+ ]
102
+ upscale_info = {k: v for k, v in zip(upscale_steps, upscale_shapes)}
103
+
104
+ for i in trange(len(sigmas) - 1, disable=disable):
105
+ denoised = model(x, sigmas[i] * s_in, **extra_args)
106
+ sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1], eta=eta)
107
+ if callback is not None:
108
+ callback({"x": x, "i": i, "sigma": sigmas[i], "sigma_hat": sigmas[i], "denoised": denoised})
109
+ if sigma_down == 0:
110
+ # Euler method
111
+ d = to_d(x, sigmas[i], denoised)
112
+ dt = sigma_down - sigmas[i]
113
+ x = x + d * dt
114
+ else:
115
+ # DPM-Solver++(2S)
116
+ t, t_next = t_fn(sigmas[i]), t_fn(sigma_down)
117
+ r = 1 / 2
118
+ h = t_next - t
119
+ s = t + r * h
120
+ x_2 = (sigma_fn(s) / sigma_fn(t)) * x - (-h * r).expm1() * denoised
121
+ denoised_2 = model(x_2, sigma_fn(s) * s_in, **extra_args)
122
+ x = (sigma_fn(t_next) / sigma_fn(t)) * x - (-h).expm1() * denoised_2
123
+ # Noise addition
124
+ if sigmas[i + 1] > 0:
125
+ # Resize
126
+ if i in upscale_info:
127
+ x = torch.nn.functional.interpolate(x, size=upscale_info[i], mode="bicubic", align_corners=False)
128
+ if unsharp_strength > 0:
129
+ blurred = gaussian_blur(x, kernel_size=unsharp_kernel_size, sigma=unsharp_sigma)
130
+ x = x + unsharp_strength * (x - blurred)
131
+ noise_sampler = default_noise_sampler(x)
132
+ noise = noise_sampler(sigmas[i], sigmas[i + 1])
133
+ x = x + noise * sigma_up * s_noise
134
+ return x
135
+
136
+
137
+ @torch.no_grad()
138
+ def sample_dpmpp_2m_sde(
139
+ model,
140
+ x,
141
+ sigmas,
142
+ extra_args=None,
143
+ callback=None,
144
+ disable=None,
145
+ eta=1.0,
146
+ s_noise=1.0,
147
+ noise_sampler=None,
148
+ solver_type="midpoint",
149
+ upscale_ratio=2.0,
150
+ start_step=5,
151
+ end_step=15,
152
+ upscale_n_step=3,
153
+ unsharp_kernel_size=3,
154
+ unsharp_sigma=0.5,
155
+ unsharp_strength=0.0,
156
+ ):
157
+ """DPM-Solver++(2M) SDE."""
158
+
159
+ if solver_type not in {"heun", "midpoint"}:
160
+ raise ValueError("solver_type must be 'heun' or 'midpoint'")
161
+
162
+ seed = extra_args.get("seed", None)
163
+ sigma_min, sigma_max = sigmas[sigmas > 0].min(), sigmas.max()
164
+ extra_args = {} if extra_args is None else extra_args
165
+ s_in = x.new_ones([x.shape[0]])
166
+
167
+ old_denoised = None
168
+ h_last = None
169
+ h = None
170
+
171
+ # make upscale info
172
+ upscale_steps = []
173
+ step = start_step - 1
174
+ while step < end_step - 1:
175
+ upscale_steps.append(step)
176
+ step += upscale_n_step
177
+ height, width = x.shape[2:]
178
+ upscale_shapes = [
179
+ (int(height * (((upscale_ratio - 1) / i) + 1)), int(width * (((upscale_ratio - 1) / i) + 1)))
180
+ for i in reversed(range(1, len(upscale_steps) + 1))
181
+ ]
182
+ upscale_info = {k: v for k, v in zip(upscale_steps, upscale_shapes)}
183
+
184
+ for i in trange(len(sigmas) - 1, disable=disable):
185
+ denoised = model(x, sigmas[i] * s_in, **extra_args)
186
+ if callback is not None:
187
+ callback({"x": x, "i": i, "sigma": sigmas[i], "sigma_hat": sigmas[i], "denoised": denoised})
188
+ if sigmas[i + 1] == 0:
189
+ # Denoising step
190
+ x = denoised
191
+ else:
192
+ # DPM-Solver++(2M) SDE
193
+ t, s = -sigmas[i].log(), -sigmas[i + 1].log()
194
+ h = s - t
195
+ eta_h = eta * h
196
+
197
+ x = sigmas[i + 1] / sigmas[i] * (-eta_h).exp() * x + (-h - eta_h).expm1().neg() * denoised
198
+
199
+ if old_denoised is not None:
200
+ r = h_last / h
201
+ if solver_type == "heun":
202
+ x = x + ((-h - eta_h).expm1().neg() / (-h - eta_h) + 1) * (1 / r) * (denoised - old_denoised)
203
+ elif solver_type == "midpoint":
204
+ x = x + 0.5 * (-h - eta_h).expm1().neg() * (1 / r) * (denoised - old_denoised)
205
+
206
+ if eta:
207
+ # Resize
208
+ if i in upscale_info:
209
+ x = torch.nn.functional.interpolate(x, size=upscale_info[i], mode="bicubic", align_corners=False)
210
+ if unsharp_strength > 0:
211
+ blurred = gaussian_blur(x, kernel_size=unsharp_kernel_size, sigma=unsharp_sigma)
212
+ x = x + unsharp_strength * (x - blurred)
213
+ denoised = None # 次ステップとサイズがあわないのでとりあえずNoneにしておく。
214
+ noise_sampler = BrownianTreeNoiseSampler(x, sigma_min, sigma_max, seed=seed, cpu=True)
215
+ x = x + noise_sampler(sigmas[i], sigmas[i + 1]) * sigmas[i + 1] * (-2 * eta_h).expm1().neg().sqrt() * s_noise
216
+
217
+ old_denoised = denoised
218
+ h_last = h
219
+ return x
220
+
221
+
222
+ @torch.no_grad()
223
+ def sample_lcm(
224
+ model,
225
+ x,
226
+ sigmas,
227
+ extra_args=None,
228
+ callback=None,
229
+ disable=None,
230
+ noise_sampler=None,
231
+ eta=None,
232
+ s_noise=None,
233
+ upscale_ratio=2.0,
234
+ start_step=5,
235
+ end_step=15,
236
+ upscale_n_step=3,
237
+ unsharp_kernel_size=3,
238
+ unsharp_sigma=0.5,
239
+ unsharp_strength=0.0,
240
+ ):
241
+ extra_args = {} if extra_args is None else extra_args
242
+ s_in = x.new_ones([x.shape[0]])
243
+
244
+ # make upscale info
245
+ upscale_steps = []
246
+ step = start_step - 1
247
+ while step < end_step - 1:
248
+ upscale_steps.append(step)
249
+ step += upscale_n_step
250
+ height, width = x.shape[2:]
251
+ upscale_shapes = [
252
+ (int(height * (((upscale_ratio - 1) / i) + 1)), int(width * (((upscale_ratio - 1) / i) + 1)))
253
+ for i in reversed(range(1, len(upscale_steps) + 1))
254
+ ]
255
+ upscale_info = {k: v for k, v in zip(upscale_steps, upscale_shapes)}
256
+
257
+ for i in trange(len(sigmas) - 1, disable=disable):
258
+ denoised = model(x, sigmas[i] * s_in, **extra_args)
259
+ if callback is not None:
260
+ callback({"x": x, "i": i, "sigma": sigmas[i], "sigma_hat": sigmas[i], "denoised": denoised})
261
+
262
+ x = denoised
263
+ if sigmas[i + 1] > 0:
264
+ # Resize
265
+ if i in upscale_info:
266
+ x = torch.nn.functional.interpolate(x, size=upscale_info[i], mode="bicubic", align_corners=False)
267
+ if unsharp_strength > 0:
268
+ blurred = gaussian_blur(x, kernel_size=unsharp_kernel_size, sigma=unsharp_sigma)
269
+ x = x + unsharp_strength * (x - blurred)
270
+ noise_sampler = default_noise_sampler(x)
271
+ x += sigmas[i + 1] * noise_sampler(sigmas[i], sigmas[i + 1])
272
+
273
+ return x
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/image.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ import torch
4
+ import numpy as np
5
+ from enum import Enum
6
+ from PIL import Image
7
+ from io import BytesIO
8
+ from typing import List, Union
9
+
10
+ import folder_paths
11
+ from .utils import install_package
12
+
13
+ # PIL to Tensor
14
+ def pil2tensor(image):
15
+ return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
16
+ # Tensor to PIL
17
+ def tensor2pil(image):
18
+ return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
19
+ # np to Tensor
20
+ def np2tensor(img_np: Union[np.ndarray, List[np.ndarray]]) -> torch.Tensor:
21
+ if isinstance(img_np, list):
22
+ return torch.cat([np2tensor(img) for img in img_np], dim=0)
23
+ return torch.from_numpy(img_np.astype(np.float32) / 255.0).unsqueeze(0)
24
+ # Tensor to np
25
+ def tensor2np(tensor: torch.Tensor) -> List[np.ndarray]:
26
+ if len(tensor.shape) == 3: # Single image
27
+ return np.clip(255.0 * tensor.cpu().numpy(), 0, 255).astype(np.uint8)
28
+ else: # Batch of images
29
+ return [np.clip(255.0 * t.cpu().numpy(), 0, 255).astype(np.uint8) for t in tensor]
30
+
31
+ def pil2byte(pil_image, format='PNG'):
32
+ byte_arr = BytesIO()
33
+ pil_image.save(byte_arr, format=format)
34
+ byte_arr.seek(0)
35
+ return byte_arr
36
+
37
+ def image2base64(image_base64):
38
+ image_bytes = base64.b64decode(image_base64)
39
+ image_data = Image.open(BytesIO(image_bytes))
40
+ return image_data
41
+
42
+ # Get new bounds
43
+ def get_new_bounds(width, height, left, right, top, bottom):
44
+ """Returns the new bounds for an image with inset crop data."""
45
+ left = 0 + left
46
+ right = width - right
47
+ top = 0 + top
48
+ bottom = height - bottom
49
+ return (left, right, top, bottom)
50
+
51
+ def RGB2RGBA(image: Image, mask: Image) -> Image:
52
+ (R, G, B) = image.convert('RGB').split()
53
+ return Image.merge('RGBA', (R, G, B, mask.convert('L')))
54
+
55
+ def image2mask(image: Image) -> torch.Tensor:
56
+ _image = image.convert('RGBA')
57
+ alpha = _image.split()[0]
58
+ bg = Image.new("L", _image.size)
59
+ _image = Image.merge('RGBA', (bg, bg, bg, alpha))
60
+ ret_mask = torch.tensor([pil2tensor(_image)[0, :, :, 3].tolist()])
61
+ return ret_mask
62
+
63
+ def mask2image(mask: torch.Tensor) -> Image:
64
+ masks = tensor2np(mask)
65
+ for m in masks:
66
+ _mask = Image.fromarray(m).convert("L")
67
+ _image = Image.new("RGBA", _mask.size, color='white')
68
+ _image = Image.composite(
69
+ _image, Image.new("RGBA", _mask.size, color='black'), _mask)
70
+ return _image
71
+
72
+ # 图像融合
73
+ class blendImage:
74
+ def g(self, x):
75
+ return torch.where(x <= 0.25, ((16 * x - 12) * x + 4) * x, torch.sqrt(x))
76
+
77
+ def blend_mode(self, img1, img2, mode):
78
+ if mode == "normal":
79
+ return img2
80
+ elif mode == "multiply":
81
+ return img1 * img2
82
+ elif mode == "screen":
83
+ return 1 - (1 - img1) * (1 - img2)
84
+ elif mode == "overlay":
85
+ return torch.where(img1 <= 0.5, 2 * img1 * img2, 1 - 2 * (1 - img1) * (1 - img2))
86
+ elif mode == "soft_light":
87
+ return torch.where(img2 <= 0.5, img1 - (1 - 2 * img2) * img1 * (1 - img1),
88
+ img1 + (2 * img2 - 1) * (self.g(img1) - img1))
89
+ elif mode == "difference":
90
+ return img1 - img2
91
+ else:
92
+ raise ValueError(f"Unsupported blend mode: {mode}")
93
+
94
+ def blend_images(self, image1: torch.Tensor, image2: torch.Tensor, blend_factor: float, blend_mode: str = 'normal'):
95
+ image2 = image2.to(image1.device)
96
+ if image1.shape != image2.shape:
97
+ image2 = image2.permute(0, 3, 1, 2)
98
+ image2 = comfy.utils.common_upscale(image2, image1.shape[2], image1.shape[1], upscale_method='bicubic',
99
+ crop='center')
100
+ image2 = image2.permute(0, 2, 3, 1)
101
+
102
+ blended_image = self.blend_mode(image1, image2, blend_mode)
103
+ blended_image = image1 * (1 - blend_factor) + blended_image * blend_factor
104
+ blended_image = torch.clamp(blended_image, 0, 1)
105
+ return blended_image
106
+
107
+
108
+ def empty_image(width, height, batch_size=1, color=0):
109
+ r = torch.full([batch_size, height, width, 1], ((color >> 16) & 0xFF) / 0xFF)
110
+ g = torch.full([batch_size, height, width, 1], ((color >> 8) & 0xFF) / 0xFF)
111
+ b = torch.full([batch_size, height, width, 1], ((color) & 0xFF) / 0xFF)
112
+ return torch.cat((r, g, b), dim=-1)
113
+
114
+
115
+ class ResizeMode(Enum):
116
+ RESIZE = "Just Resize"
117
+ INNER_FIT = "Crop and Resize"
118
+ OUTER_FIT = "Resize and Fill"
119
+ def int_value(self):
120
+ if self == ResizeMode.RESIZE:
121
+ return 0
122
+ elif self == ResizeMode.INNER_FIT:
123
+ return 1
124
+ elif self == ResizeMode.OUTER_FIT:
125
+ return 2
126
+ assert False, "NOTREACHED"
127
+
128
+ # credit by https://github.com/chflame163/ComfyUI_LayerStyle/blob/main/py/imagefunc.py#L591C1-L617C22
129
+ def fit_resize_image(image: Image, target_width: int, target_height: int, fit: str, resize_sampler: str,
130
+ background_color: str = '#000000') -> Image:
131
+ image = image.convert('RGB')
132
+ orig_width, orig_height = image.size
133
+ if image is not None:
134
+ if fit == 'letterbox':
135
+ if orig_width / orig_height > target_width / target_height: # 更宽,上下留黑
136
+ fit_width = target_width
137
+ fit_height = int(target_width / orig_width * orig_height)
138
+ else: # 更瘦,左右留黑
139
+ fit_height = target_height
140
+ fit_width = int(target_height / orig_height * orig_width)
141
+ fit_image = image.resize((fit_width, fit_height), resize_sampler)
142
+ ret_image = Image.new('RGB', size=(target_width, target_height), color=background_color)
143
+ ret_image.paste(fit_image, box=((target_width - fit_width) // 2, (target_height - fit_height) // 2))
144
+ elif fit == 'crop':
145
+ if orig_width / orig_height > target_width / target_height: # 更宽,裁左右
146
+ fit_width = int(orig_height * target_width / target_height)
147
+ fit_image = image.crop(
148
+ ((orig_width - fit_width) // 2, 0, (orig_width - fit_width) // 2 + fit_width, orig_height))
149
+ else: # 更瘦,裁上下
150
+ fit_height = int(orig_width * target_height / target_width)
151
+ fit_image = image.crop(
152
+ (0, (orig_height - fit_height) // 2, orig_width, (orig_height - fit_height) // 2 + fit_height))
153
+ ret_image = fit_image.resize((target_width, target_height), resize_sampler)
154
+ else:
155
+ ret_image = image.resize((target_width, target_height), resize_sampler)
156
+ return ret_image
157
+
158
+ # CLIP反推
159
+ import comfy.utils
160
+ from torchvision import transforms
161
+ Config, Interrogator = None, None
162
+ class CI_Inference:
163
+ ci_model = None
164
+ cache_path: str
165
+
166
+ def __init__(self):
167
+ self.ci_model = None
168
+ self.low_vram = False
169
+ self.cache_path = os.path.join(folder_paths.models_dir, "clip_interrogator")
170
+
171
+ def _load_model(self, model_name, low_vram=False):
172
+ if not (self.ci_model and model_name == self.ci_model.config.clip_model_name and self.low_vram == low_vram):
173
+ self.low_vram = low_vram
174
+ print(f"Load model: {model_name}")
175
+
176
+ config = Config(
177
+ device="cuda" if torch.cuda.is_available() else "cpu",
178
+ download_cache=True,
179
+ clip_model_name=model_name,
180
+ clip_model_path=self.cache_path,
181
+ cache_path=self.cache_path,
182
+ caption_model_name='blip-large'
183
+ )
184
+
185
+ if low_vram:
186
+ config.apply_low_vram_defaults()
187
+
188
+ self.ci_model = Interrogator(config)
189
+
190
+ def _interrogate(self, image, mode, caption=None):
191
+ if mode == 'best':
192
+ prompt = self.ci_model.interrogate(image, caption=caption)
193
+ elif mode == 'classic':
194
+ prompt = self.ci_model.interrogate_classic(image, caption=caption)
195
+ elif mode == 'fast':
196
+ prompt = self.ci_model.interrogate_fast(image, caption=caption)
197
+ elif mode == 'negative':
198
+ prompt = self.ci_model.interrogate_negative(image)
199
+ else:
200
+ raise Exception(f"Unknown mode {mode}")
201
+ return prompt
202
+
203
+ def image_to_prompt(self, image, mode, model_name='ViT-L-14/openai', low_vram=False):
204
+ try:
205
+ from clip_interrogator import Config, Interrogator
206
+ global Config, Interrogator
207
+ except:
208
+ install_package("clip_interrogator", "0.6.0")
209
+ from clip_interrogator import Config, Interrogator
210
+
211
+ pbar = comfy.utils.ProgressBar(len(image))
212
+
213
+ self._load_model(model_name, low_vram)
214
+ prompt = []
215
+ for i in range(len(image)):
216
+ im = image[i]
217
+
218
+ im = tensor2pil(im)
219
+ im = im.convert('RGB')
220
+
221
+ _prompt = self._interrogate(im, mode)
222
+ pbar.update(1)
223
+ prompt.append(_prompt)
224
+
225
+ return prompt
226
+
227
+ ci = CI_Inference()
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/lllite.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ import comfy
4
+
5
+
6
+ def extra_options_to_module_prefix(extra_options):
7
+ # extra_options = {'transformer_index': 2, 'block_index': 8, 'original_shape': [2, 4, 128, 128], 'block': ('input', 7), 'n_heads': 20, 'dim_head': 64}
8
+
9
+ # block is: [('input', 4), ('input', 5), ('input', 7), ('input', 8), ('middle', 0),
10
+ # ('output', 0), ('output', 1), ('output', 2), ('output', 3), ('output', 4), ('output', 5)]
11
+ # transformer_index is: [0, 1, 2, 3, 4, 5, 6, 7, 8], for each block
12
+ # block_index is: 0-1 or 0-9, depends on the block
13
+ # input 7 and 8, middle has 10 blocks
14
+
15
+ # make module name from extra_options
16
+ block = extra_options["block"]
17
+ block_index = extra_options["block_index"]
18
+ if block[0] == "input":
19
+ module_pfx = f"lllite_unet_input_blocks_{block[1]}_1_transformer_blocks_{block_index}"
20
+ elif block[0] == "middle":
21
+ module_pfx = f"lllite_unet_middle_block_1_transformer_blocks_{block_index}"
22
+ elif block[0] == "output":
23
+ module_pfx = f"lllite_unet_output_blocks_{block[1]}_1_transformer_blocks_{block_index}"
24
+ else:
25
+ raise Exception("invalid block name")
26
+ return module_pfx
27
+
28
+
29
+ def load_control_net_lllite_patch(path, cond_image, multiplier, num_steps, start_percent, end_percent):
30
+ # calculate start and end step
31
+ start_step = math.floor(num_steps * start_percent * 0.01) if start_percent > 0 else 0
32
+ end_step = math.floor(num_steps * end_percent * 0.01) if end_percent > 0 else num_steps
33
+
34
+ # load weights
35
+ ctrl_sd = comfy.utils.load_torch_file(path, safe_load=True)
36
+
37
+ # split each weights for each module
38
+ module_weights = {}
39
+ for key, value in ctrl_sd.items():
40
+ fragments = key.split(".")
41
+ module_name = fragments[0]
42
+ weight_name = ".".join(fragments[1:])
43
+
44
+ if module_name not in module_weights:
45
+ module_weights[module_name] = {}
46
+ module_weights[module_name][weight_name] = value
47
+
48
+ # load each module
49
+ modules = {}
50
+ for module_name, weights in module_weights.items():
51
+ # ここの自動判定を何とかしたい
52
+ if "conditioning1.4.weight" in weights:
53
+ depth = 3
54
+ elif weights["conditioning1.2.weight"].shape[-1] == 4:
55
+ depth = 2
56
+ else:
57
+ depth = 1
58
+
59
+ module = LLLiteModule(
60
+ name=module_name,
61
+ is_conv2d=weights["down.0.weight"].ndim == 4,
62
+ in_dim=weights["down.0.weight"].shape[1],
63
+ depth=depth,
64
+ cond_emb_dim=weights["conditioning1.0.weight"].shape[0] * 2,
65
+ mlp_dim=weights["down.0.weight"].shape[0],
66
+ multiplier=multiplier,
67
+ num_steps=num_steps,
68
+ start_step=start_step,
69
+ end_step=end_step,
70
+ )
71
+ info = module.load_state_dict(weights)
72
+ modules[module_name] = module
73
+ if len(modules) == 1:
74
+ module.is_first = True
75
+
76
+ print(f"loaded {path} successfully, {len(modules)} modules")
77
+
78
+ # cond imageをセットする
79
+ cond_image = cond_image.permute(0, 3, 1, 2) # b,h,w,3 -> b,3,h,w
80
+ cond_image = cond_image * 2.0 - 1.0 # 0-1 -> -1-+1
81
+
82
+ for module in modules.values():
83
+ module.set_cond_image(cond_image)
84
+
85
+ class control_net_lllite_patch:
86
+ def __init__(self, modules):
87
+ self.modules = modules
88
+
89
+ def __call__(self, q, k, v, extra_options):
90
+ module_pfx = extra_options_to_module_prefix(extra_options)
91
+
92
+ is_attn1 = q.shape[-1] == k.shape[-1] # self attention
93
+ if is_attn1:
94
+ module_pfx = module_pfx + "_attn1"
95
+ else:
96
+ module_pfx = module_pfx + "_attn2"
97
+
98
+ module_pfx_to_q = module_pfx + "_to_q"
99
+ module_pfx_to_k = module_pfx + "_to_k"
100
+ module_pfx_to_v = module_pfx + "_to_v"
101
+
102
+ if module_pfx_to_q in self.modules:
103
+ q = q + self.modules[module_pfx_to_q](q)
104
+ if module_pfx_to_k in self.modules:
105
+ k = k + self.modules[module_pfx_to_k](k)
106
+ if module_pfx_to_v in self.modules:
107
+ v = v + self.modules[module_pfx_to_v](v)
108
+
109
+ return q, k, v
110
+
111
+ def to(self, device):
112
+ for d in self.modules.keys():
113
+ self.modules[d] = self.modules[d].to(device)
114
+ return self
115
+
116
+ return control_net_lllite_patch(modules)
117
+
118
+ class LLLiteModule(torch.nn.Module):
119
+ def __init__(
120
+ self,
121
+ name: str,
122
+ is_conv2d: bool,
123
+ in_dim: int,
124
+ depth: int,
125
+ cond_emb_dim: int,
126
+ mlp_dim: int,
127
+ multiplier: int,
128
+ num_steps: int,
129
+ start_step: int,
130
+ end_step: int,
131
+ ):
132
+ super().__init__()
133
+ self.name = name
134
+ self.is_conv2d = is_conv2d
135
+ self.multiplier = multiplier
136
+ self.num_steps = num_steps
137
+ self.start_step = start_step
138
+ self.end_step = end_step
139
+ self.is_first = False
140
+
141
+ modules = []
142
+ modules.append(torch.nn.Conv2d(3, cond_emb_dim // 2, kernel_size=4, stride=4, padding=0)) # to latent (from VAE) size*2
143
+ if depth == 1:
144
+ modules.append(torch.nn.ReLU(inplace=True))
145
+ modules.append(torch.nn.Conv2d(cond_emb_dim // 2, cond_emb_dim, kernel_size=2, stride=2, padding=0))
146
+ elif depth == 2:
147
+ modules.append(torch.nn.ReLU(inplace=True))
148
+ modules.append(torch.nn.Conv2d(cond_emb_dim // 2, cond_emb_dim, kernel_size=4, stride=4, padding=0))
149
+ elif depth == 3:
150
+ # kernel size 8は大きすぎるので、4にする / kernel size 8 is too large, so set it to 4
151
+ modules.append(torch.nn.ReLU(inplace=True))
152
+ modules.append(torch.nn.Conv2d(cond_emb_dim // 2, cond_emb_dim // 2, kernel_size=4, stride=4, padding=0))
153
+ modules.append(torch.nn.ReLU(inplace=True))
154
+ modules.append(torch.nn.Conv2d(cond_emb_dim // 2, cond_emb_dim, kernel_size=2, stride=2, padding=0))
155
+
156
+ self.conditioning1 = torch.nn.Sequential(*modules)
157
+
158
+ if self.is_conv2d:
159
+ self.down = torch.nn.Sequential(
160
+ torch.nn.Conv2d(in_dim, mlp_dim, kernel_size=1, stride=1, padding=0),
161
+ torch.nn.ReLU(inplace=True),
162
+ )
163
+ self.mid = torch.nn.Sequential(
164
+ torch.nn.Conv2d(mlp_dim + cond_emb_dim, mlp_dim, kernel_size=1, stride=1, padding=0),
165
+ torch.nn.ReLU(inplace=True),
166
+ )
167
+ self.up = torch.nn.Sequential(
168
+ torch.nn.Conv2d(mlp_dim, in_dim, kernel_size=1, stride=1, padding=0),
169
+ )
170
+ else:
171
+ self.down = torch.nn.Sequential(
172
+ torch.nn.Linear(in_dim, mlp_dim),
173
+ torch.nn.ReLU(inplace=True),
174
+ )
175
+ self.mid = torch.nn.Sequential(
176
+ torch.nn.Linear(mlp_dim + cond_emb_dim, mlp_dim),
177
+ torch.nn.ReLU(inplace=True),
178
+ )
179
+ self.up = torch.nn.Sequential(
180
+ torch.nn.Linear(mlp_dim, in_dim),
181
+ )
182
+
183
+ self.depth = depth
184
+ self.cond_image = None
185
+ self.cond_emb = None
186
+ self.current_step = 0
187
+
188
+ # @torch.inference_mode()
189
+ def set_cond_image(self, cond_image):
190
+ # print("set_cond_image", self.name)
191
+ self.cond_image = cond_image
192
+ self.cond_emb = None
193
+ self.current_step = 0
194
+
195
+ def forward(self, x):
196
+ if self.num_steps > 0:
197
+ if self.current_step < self.start_step:
198
+ self.current_step += 1
199
+ return torch.zeros_like(x)
200
+ elif self.current_step >= self.end_step:
201
+ if self.is_first and self.current_step == self.end_step:
202
+ print(f"end LLLite: step {self.current_step}")
203
+ self.current_step += 1
204
+ if self.current_step >= self.num_steps:
205
+ self.current_step = 0 # reset
206
+ return torch.zeros_like(x)
207
+ else:
208
+ if self.is_first and self.current_step == self.start_step:
209
+ print(f"start LLLite: step {self.current_step}")
210
+ self.current_step += 1
211
+ if self.current_step >= self.num_steps:
212
+ self.current_step = 0 # reset
213
+
214
+ if self.cond_emb is None:
215
+ # print(f"cond_emb is None, {self.name}")
216
+ cx = self.conditioning1(self.cond_image.to(x.device, dtype=x.dtype))
217
+ if not self.is_conv2d:
218
+ # reshape / b,c,h,w -> b,h*w,c
219
+ n, c, h, w = cx.shape
220
+ cx = cx.view(n, c, h * w).permute(0, 2, 1)
221
+ self.cond_emb = cx
222
+
223
+ cx = self.cond_emb
224
+ # print(f"forward {self.name}, {cx.shape}, {x.shape}")
225
+
226
+ # uncond/condでxはバッチサイズが2倍
227
+ if x.shape[0] != cx.shape[0]:
228
+ if self.is_conv2d:
229
+ cx = cx.repeat(x.shape[0] // cx.shape[0], 1, 1, 1)
230
+ else:
231
+ # print("x.shape[0] != cx.shape[0]", x.shape[0], cx.shape[0])
232
+ cx = cx.repeat(x.shape[0] // cx.shape[0], 1, 1)
233
+
234
+ cx = torch.cat([cx, self.down(x)], dim=1 if self.is_conv2d else 2)
235
+ cx = self.mid(cx)
236
+ cx = self.up(cx)
237
+ return cx * self.multiplier
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/loader.py ADDED
@@ -0,0 +1,570 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re, time, os, psutil
2
+ import folder_paths
3
+ import comfy.utils
4
+ import comfy.sd
5
+ import comfy.controlnet
6
+
7
+ from comfy.model_patcher import ModelPatcher
8
+ from nodes import NODE_CLASS_MAPPINGS
9
+ from collections import defaultdict
10
+ from .log import log_node_info, log_node_error
11
+ from ..modules.dit.pixArt.loader import load_pixart
12
+
13
+ diffusion_loaders = ["easy fullLoader", "easy a1111Loader", "easy fluxLoader", "easy comfyLoader", "easy hunyuanDiTLoader", "easy zero123Loader", "easy svdLoader"]
14
+ stable_cascade_loaders = ["easy cascadeLoader"]
15
+ dit_loaders = ['easy pixArtLoader']
16
+ controlnet_loaders = ["easy controlnetLoader", "easy controlnetLoaderADV", "easy controlnetLoader++"]
17
+ instant_loaders = ["easy instantIDApply", "easy instantIDApplyADV"]
18
+ cascade_vae_node = ["easy preSamplingCascade", "easy fullCascadeKSampler"]
19
+ model_merge_node = ["easy XYInputs: ModelMergeBlocks"]
20
+ lora_widget = ["easy fullLoader", "easy a1111Loader", "easy comfyLoader", "easy fluxLoader"]
21
+
22
+ class easyLoader:
23
+ def __init__(self):
24
+ self.loaded_objects = {
25
+ "ckpt": defaultdict(tuple), # {ckpt_name: (model, ...)}
26
+ "unet": defaultdict(tuple),
27
+ "clip": defaultdict(tuple),
28
+ "clip_vision": defaultdict(tuple),
29
+ "bvae": defaultdict(tuple),
30
+ "vae": defaultdict(object),
31
+ "lora": defaultdict(dict), # {lora_name: {UID: (model_lora, clip_lora)}}
32
+ "controlnet": defaultdict(dict),
33
+ "t5": defaultdict(tuple),
34
+ "chatglm3": defaultdict(tuple),
35
+ }
36
+ self.memory_threshold = self.determine_memory_threshold(1)
37
+ self.lora_name_cache = []
38
+
39
+ def clean_values(self, values: str):
40
+ original_values = values.split("; ")
41
+ cleaned_values = []
42
+
43
+ for value in original_values:
44
+ cleaned_value = value.strip(';').strip()
45
+ if cleaned_value == "":
46
+ continue
47
+ try:
48
+ cleaned_value = int(cleaned_value)
49
+ except ValueError:
50
+ try:
51
+ cleaned_value = float(cleaned_value)
52
+ except ValueError:
53
+ pass
54
+ cleaned_values.append(cleaned_value)
55
+
56
+ return cleaned_values
57
+
58
+ def clear_unused_objects(self, desired_names: set, object_type: str):
59
+ keys = set(self.loaded_objects[object_type].keys())
60
+ for key in keys - desired_names:
61
+ del self.loaded_objects[object_type][key]
62
+
63
+ def get_input_value(self, entry, key, prompt=None):
64
+ val = entry["inputs"][key]
65
+ if isinstance(val, str):
66
+ return val
67
+ elif isinstance(val, list):
68
+ if prompt is not None and val[0]:
69
+ return prompt[val[0]]['inputs'][key]
70
+ else:
71
+ return val[0]
72
+ else:
73
+ return str(val)
74
+
75
+ def process_pipe_loader(self, entry, desired_ckpt_names, desired_vae_names, desired_lora_names, desired_lora_settings, num_loras=3, suffix=""):
76
+ for idx in range(1, num_loras + 1):
77
+ lora_name_key = f"{suffix}lora{idx}_name"
78
+ desired_lora_names.add(self.get_input_value(entry, lora_name_key))
79
+ setting = f'{self.get_input_value(entry, lora_name_key)};{entry["inputs"][f"{suffix}lora{idx}_model_strength"]};{entry["inputs"][f"{suffix}lora{idx}_clip_strength"]}'
80
+ desired_lora_settings.add(setting)
81
+
82
+ desired_ckpt_names.add(self.get_input_value(entry, f"{suffix}ckpt_name"))
83
+ desired_vae_names.add(self.get_input_value(entry, f"{suffix}vae_name"))
84
+
85
+ def update_loaded_objects(self, prompt):
86
+ desired_ckpt_names = set()
87
+ desired_unet_names = set()
88
+ desired_clip_names = set()
89
+ desired_vae_names = set()
90
+ desired_lora_names = set()
91
+ desired_lora_settings = set()
92
+ desired_controlnet_names = set()
93
+ desired_t5_names = set()
94
+ desired_glm3_names = set()
95
+
96
+ for entry in prompt.values():
97
+ class_type = entry["class_type"]
98
+ if class_type in lora_widget:
99
+ lora_name = self.get_input_value(entry, "lora_name")
100
+ desired_lora_names.add(lora_name)
101
+ setting = f'{lora_name};{entry["inputs"]["lora_model_strength"]};{entry["inputs"]["lora_clip_strength"]}'
102
+ desired_lora_settings.add(setting)
103
+
104
+ if class_type in diffusion_loaders:
105
+ desired_ckpt_names.add(self.get_input_value(entry, "ckpt_name", prompt))
106
+ desired_vae_names.add(self.get_input_value(entry, "vae_name"))
107
+
108
+ elif class_type in ['easy kolorsLoader']:
109
+ desired_unet_names.add(self.get_input_value(entry, "unet_name"))
110
+ desired_vae_names.add(self.get_input_value(entry, "vae_name"))
111
+ desired_glm3_names.add(self.get_input_value(entry, "chatglm3_name"))
112
+
113
+ elif class_type in dit_loaders:
114
+ t5_name = self.get_input_value(entry, "mt5_name") if "mt5_name" in entry["inputs"] else None
115
+ clip_name = self.get_input_value(entry, "clip_name") if "clip_name" in entry["inputs"] else None
116
+ model_name = self.get_input_value(entry, "model_name")
117
+ ckpt_name = self.get_input_value(entry, "ckpt_name", prompt)
118
+ if t5_name:
119
+ desired_t5_names.add(t5_name)
120
+ if clip_name:
121
+ desired_clip_names.add(clip_name)
122
+ desired_ckpt_names.add(ckpt_name+'_'+model_name)
123
+
124
+ elif class_type in stable_cascade_loaders:
125
+ desired_unet_names.add(self.get_input_value(entry, "stage_c"))
126
+ desired_unet_names.add(self.get_input_value(entry, "stage_b"))
127
+ desired_clip_names.add(self.get_input_value(entry, "clip_name"))
128
+ desired_vae_names.add(self.get_input_value(entry, "stage_a"))
129
+
130
+ elif class_type in cascade_vae_node:
131
+ encode_vae_name = self.get_input_value(entry, "encode_vae_name")
132
+ decode_vae_name = self.get_input_value(entry, "decode_vae_name")
133
+ if encode_vae_name and encode_vae_name != 'None':
134
+ desired_vae_names.add(encode_vae_name)
135
+ if decode_vae_name and decode_vae_name != 'None':
136
+ desired_vae_names.add(decode_vae_name)
137
+
138
+ elif class_type in controlnet_loaders:
139
+ control_net_name = self.get_input_value(entry, "control_net_name", prompt)
140
+ scale_soft_weights = self.get_input_value(entry, "scale_soft_weights")
141
+ desired_controlnet_names.add(f'{control_net_name};{scale_soft_weights}')
142
+
143
+ elif class_type in instant_loaders:
144
+ control_net_name = self.get_input_value(entry, "control_net_name", prompt)
145
+ scale_soft_weights = self.get_input_value(entry, "cn_soft_weights")
146
+ desired_controlnet_names.add(f'{control_net_name};{scale_soft_weights}')
147
+
148
+ elif class_type in model_merge_node:
149
+ desired_ckpt_names.add(self.get_input_value(entry, "ckpt_name_1"))
150
+ desired_ckpt_names.add(self.get_input_value(entry, "ckpt_name_2"))
151
+ vae_use = self.get_input_value(entry, "vae_use")
152
+ if vae_use != 'Use Model 1' and vae_use != 'Use Model 2':
153
+ desired_vae_names.add(vae_use)
154
+
155
+ object_types = ["ckpt", "unet", "clip", "bvae", "vae", "lora", "controlnet", "t5"]
156
+ for object_type in object_types:
157
+ if object_type == 'unet':
158
+ desired_names = desired_unet_names
159
+ elif object_type in ["ckpt", "clip", "bvae"]:
160
+ if object_type == 'clip':
161
+ desired_names = desired_ckpt_names.union(desired_clip_names)
162
+ else:
163
+ desired_names = desired_ckpt_names
164
+ elif object_type == "vae":
165
+ desired_names = desired_vae_names
166
+ elif object_type == "controlnet":
167
+ desired_names = desired_controlnet_names
168
+ elif object_type == "t5":
169
+ desired_names = desired_t5_names
170
+ elif object_type == "chatglm3":
171
+ desired_names = desired_glm3_names
172
+ else:
173
+ desired_names = desired_lora_names
174
+ self.clear_unused_objects(desired_names, object_type)
175
+
176
+ def add_to_cache(self, obj_type, key, value):
177
+ """
178
+ Add an item to the cache with the current timestamp.
179
+ """
180
+ timestamped_value = (value, time.time())
181
+ self.loaded_objects[obj_type][key] = timestamped_value
182
+
183
+ def determine_memory_threshold(self, percentage=0.8):
184
+ """
185
+ Determines the memory threshold as a percentage of the total available memory.
186
+ Args:
187
+ - percentage (float): The fraction of total memory to use as the threshold.
188
+ Should be a value between 0 and 1. Default is 0.8 (80%).
189
+ Returns:
190
+ - memory_threshold (int): Memory threshold in bytes.
191
+ """
192
+ total_memory = psutil.virtual_memory().total
193
+ memory_threshold = total_memory * percentage
194
+ return memory_threshold
195
+
196
+ def get_memory_usage(self):
197
+ """
198
+ Returns the memory usage of the current process in bytes.
199
+ """
200
+ process = psutil.Process(os.getpid())
201
+ return process.memory_info().rss
202
+
203
+ def eviction_based_on_memory(self):
204
+ """
205
+ Evicts objects from cache based on memory usage and priority.
206
+ """
207
+ current_memory = self.get_memory_usage()
208
+ if current_memory < self.memory_threshold:
209
+ return
210
+ eviction_order = ["vae", "lora", "bvae", "clip", "ckpt", "controlnet", "unet", "t5", "chatglm3"]
211
+ for obj_type in eviction_order:
212
+ if current_memory < self.memory_threshold:
213
+ break
214
+ # Sort items based on age (using the timestamp)
215
+ items = list(self.loaded_objects[obj_type].items())
216
+ items.sort(key=lambda x: x[1][1]) # Sorting by timestamp
217
+
218
+ for item in items:
219
+ if current_memory < self.memory_threshold:
220
+ break
221
+ del self.loaded_objects[obj_type][item[0]]
222
+ current_memory = self.get_memory_usage()
223
+
224
+ def load_checkpoint(self, ckpt_name, config_name=None, load_vision=False):
225
+ cache_name = ckpt_name
226
+ if config_name not in [None, "Default"]:
227
+ cache_name = ckpt_name + "_" + config_name
228
+ if cache_name in self.loaded_objects["ckpt"]:
229
+ clip_vision = self.loaded_objects["clip_vision"][cache_name][0] if load_vision else None
230
+ clip = self.loaded_objects["clip"][cache_name][0] if not load_vision else None
231
+ return self.loaded_objects["ckpt"][cache_name][0], clip, self.loaded_objects["bvae"][cache_name][0], clip_vision
232
+
233
+ ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
234
+
235
+ output_clip = False if load_vision else True
236
+ output_clipvision = True if load_vision else False
237
+ if config_name not in [None, "Default"]:
238
+ config_path = folder_paths.get_full_path("configs", config_name)
239
+ loaded_ckpt = comfy.sd.load_checkpoint(config_path, ckpt_path, output_vae=True, output_clip=output_clip, embedding_directory=folder_paths.get_folder_paths("embeddings"))
240
+ else:
241
+ model_options = {}
242
+ if re.search("nf4", ckpt_name):
243
+ from ..modules.bitsandbytes_NF4 import OPS
244
+ model_options = {"custom_operations": OPS}
245
+ loaded_ckpt = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=output_clip, output_clipvision=output_clipvision, embedding_directory=folder_paths.get_folder_paths("embeddings"), model_options=model_options)
246
+
247
+ self.add_to_cache("ckpt", cache_name, loaded_ckpt[0])
248
+ self.add_to_cache("bvae", cache_name, loaded_ckpt[2])
249
+
250
+ clip = loaded_ckpt[1]
251
+ clip_vision = loaded_ckpt[3]
252
+ if clip:
253
+ self.add_to_cache("clip", cache_name, clip)
254
+ if clip_vision:
255
+ self.add_to_cache("clip_vision", cache_name, clip_vision)
256
+
257
+ self.eviction_based_on_memory()
258
+
259
+ return loaded_ckpt[0], clip, loaded_ckpt[2], clip_vision
260
+
261
+ def load_vae(self, vae_name):
262
+ if vae_name in self.loaded_objects["vae"]:
263
+ return self.loaded_objects["vae"][vae_name][0]
264
+
265
+ vae_path = folder_paths.get_full_path("vae", vae_name)
266
+ sd = comfy.utils.load_torch_file(vae_path)
267
+ loaded_vae = comfy.sd.VAE(sd=sd)
268
+ self.add_to_cache("vae", vae_name, loaded_vae)
269
+ self.eviction_based_on_memory()
270
+
271
+ return loaded_vae
272
+
273
+ def load_unet(self, unet_name):
274
+ if unet_name in self.loaded_objects["unet"]:
275
+ log_node_info("Load UNet", f"{unet_name} cached")
276
+ return self.loaded_objects["unet"][unet_name][0]
277
+
278
+ unet_path = folder_paths.get_full_path("unet", unet_name)
279
+ model = comfy.sd.load_unet(unet_path)
280
+ self.add_to_cache("unet", unet_name, model)
281
+ self.eviction_based_on_memory()
282
+
283
+ return model
284
+
285
+ def load_controlnet(self, control_net_name, scale_soft_weights=1, use_cache=True):
286
+ unique_id = f'{control_net_name};{str(scale_soft_weights)}'
287
+ if use_cache and unique_id in self.loaded_objects["controlnet"]:
288
+ return self.loaded_objects["controlnet"][unique_id][0]
289
+ if scale_soft_weights < 1:
290
+ if "ScaledSoftControlNetWeights" in NODE_CLASS_MAPPINGS:
291
+ soft_weight_cls = NODE_CLASS_MAPPINGS['ScaledSoftControlNetWeights']
292
+ (weights, timestep_keyframe) = soft_weight_cls().load_weights(scale_soft_weights, False)
293
+ cn_adv_cls = NODE_CLASS_MAPPINGS['ControlNetLoaderAdvanced']
294
+ control_net, = cn_adv_cls().load_controlnet(control_net_name, timestep_keyframe)
295
+ else:
296
+ raise Exception(f"[Advanced-ControlNet Not Found] you need to install 'COMFYUI-Advanced-ControlNet'")
297
+ else:
298
+ controlnet_path = folder_paths.get_full_path("controlnet", control_net_name)
299
+ control_net = comfy.controlnet.load_controlnet(controlnet_path)
300
+ if use_cache:
301
+ self.add_to_cache("controlnet", unique_id, control_net)
302
+ self.eviction_based_on_memory()
303
+
304
+ return control_net
305
+ def load_clip(self, clip_name, type='stable_diffusion', load_clip=None):
306
+ if clip_name in self.loaded_objects["clip"]:
307
+ return self.loaded_objects["clip"][clip_name][0]
308
+
309
+ if type == 'stable_diffusion':
310
+ clip_type = comfy.sd.CLIPType.STABLE_DIFFUSION
311
+ elif type == 'stable_cascade':
312
+ clip_type = comfy.sd.CLIPType.STABLE_CASCADE
313
+ elif type == 'sd3':
314
+ clip_type = comfy.sd.CLIPType.SD3
315
+ elif type == 'flux':
316
+ clip_type = comfy.sd.CLIPType.FLUX
317
+ elif type == 'stable_audio':
318
+ clip_type = comfy.sd.CLIPType.STABLE_AUDIO
319
+ clip_path = folder_paths.get_full_path("clip", clip_name)
320
+ load_clip = comfy.sd.load_clip(ckpt_paths=[clip_path], embedding_directory=folder_paths.get_folder_paths("embeddings"), clip_type=clip_type)
321
+ self.add_to_cache("clip", clip_name, load_clip)
322
+ self.eviction_based_on_memory()
323
+
324
+ return load_clip
325
+
326
+ def load_lora(self, lora, model=None, clip=None, type=None , use_cache=True):
327
+ lora_name = lora["lora_name"]
328
+ model = model if model is not None else lora["model"]
329
+ clip = clip if clip is not None else lora["clip"]
330
+ model_strength = lora["model_strength"]
331
+ clip_strength = lora["clip_strength"]
332
+ lbw = lora["lbw"] if "lbw" in lora else None
333
+ lbw_a = lora["lbw_a"] if "lbw_a" in lora else None
334
+ lbw_b = lora["lbw_b"] if "lbw_b" in lora else None
335
+
336
+ model_hash = str(model)[44:-1]
337
+ clip_hash = str(clip)[25:-1] if clip else ''
338
+
339
+ unique_id = f'{model_hash};{clip_hash};{lora_name};{model_strength};{clip_strength}'
340
+
341
+ if use_cache and unique_id in self.loaded_objects["lora"]:
342
+ log_node_info("Load LORA",f"{lora_name} cached")
343
+ return self.loaded_objects["lora"][unique_id][0]
344
+
345
+ orig_lora_name = lora_name
346
+ lora_name = self.resolve_lora_name(lora_name)
347
+
348
+ if lora_name is not None:
349
+ lora_path = folder_paths.get_full_path("loras", lora_name)
350
+ else:
351
+ lora_path = None
352
+
353
+ if lora_path is not None:
354
+ log_node_info("Load LORA",f"{lora_name}: model={model_strength:.3f}, clip={clip_strength:.3f}, LBW={lbw}, A={lbw_a}, B={lbw_b}")
355
+ if lbw:
356
+ lbw = lora["lbw"]
357
+ lbw_a = lora["lbw_a"]
358
+ lbw_b = lora["lbw_b"]
359
+ if 'LoraLoaderBlockWeight //Inspire' not in NODE_CLASS_MAPPINGS:
360
+ raise Exception('[InspirePack Not Found] you need to install ComfyUI-Inspire-Pack')
361
+ cls = NODE_CLASS_MAPPINGS['LoraLoaderBlockWeight //Inspire']
362
+ model, clip, _ = cls().doit(model, clip, lora_name, model_strength, clip_strength, False, 0,
363
+ lbw_a, lbw_b, "", lbw)
364
+ else:
365
+ _lora = comfy.utils.load_torch_file(lora_path, safe_load=True)
366
+ keys = _lora.keys()
367
+ if "down_blocks.0.resnets.0.norm1.bias" in keys:
368
+ print('Using LORA for Resadapter')
369
+ key_map = {}
370
+ key_map = comfy.lora.model_lora_keys_unet(model.model, key_map)
371
+ mapping_norm = {}
372
+
373
+ for key in keys:
374
+ if ".weight" in key:
375
+ key_name_in_ori_sd = key_map[key.replace(".weight", "")]
376
+ mapping_norm[key_name_in_ori_sd] = _lora[key]
377
+ elif ".bias" in key:
378
+ key_name_in_ori_sd = key_map[key.replace(".bias", "")]
379
+ mapping_norm[key_name_in_ori_sd.replace(".weight", ".bias")] = _lora[
380
+ key
381
+ ]
382
+ else:
383
+ print("===>Unexpected key", key)
384
+ mapping_norm[key] = _lora[key]
385
+
386
+ for k in mapping_norm.keys():
387
+ if k not in model.model.state_dict():
388
+ print("===>Missing key:", k)
389
+ model.model.load_state_dict(mapping_norm, strict=False)
390
+ return (model, clip)
391
+
392
+ # PixArt
393
+ if type is not None and type == 'PixArt':
394
+ from ..modules.dit.pixArt.loader import load_pixart_lora
395
+ model = load_pixart_lora(model, _lora, lora_path, model_strength)
396
+ else:
397
+ model, clip = comfy.sd.load_lora_for_models(model, clip, _lora, model_strength, clip_strength)
398
+
399
+ if use_cache:
400
+ self.add_to_cache("lora", unique_id, (model, clip))
401
+ self.eviction_based_on_memory()
402
+ else:
403
+ log_node_error(f"LORA NOT FOUND", orig_lora_name)
404
+
405
+ return model, clip
406
+
407
+ def resolve_lora_name(self, name):
408
+ if os.path.exists(name):
409
+ return name
410
+ else:
411
+ if len(self.lora_name_cache) == 0:
412
+ loras = folder_paths.get_filename_list("loras")
413
+ self.lora_name_cache.extend(loras)
414
+ for x in self.lora_name_cache:
415
+ if x.endswith(name):
416
+ return x
417
+
418
+ # 如果刷新网页后新添加的lora走这个逻辑
419
+ log_node_info("LORA NOT IN CACHE", f"{name}")
420
+ loras = folder_paths.get_filename_list("loras")
421
+ for x in loras:
422
+ if x.endswith(name):
423
+ self.lora_name_cache.append(x)
424
+ return x
425
+
426
+ return None
427
+
428
+ def load_main(self, ckpt_name, config_name, vae_name, lora_name, lora_model_strength, lora_clip_strength, optional_lora_stack, model_override, clip_override, vae_override, prompt, nf4=False):
429
+ model: ModelPatcher | None = None
430
+ clip: comfy.sd.CLIP | None = None
431
+ vae: comfy.sd.VAE | None = None
432
+ clip_vision = None
433
+ lora_stack = []
434
+
435
+ # Check for model override
436
+ can_load_lora = True
437
+ # 判断是否存在 模型或Lora叠加xyplot, 若存在优先缓存第一个模型
438
+ # Determine whether there is a model or Lora overlapping xyplot, and if there is, prioritize caching the first model.
439
+ xy_model_id = next((x for x in prompt if str(prompt[x]["class_type"]) in ["easy XYInputs: ModelMergeBlocks",
440
+ "easy XYInputs: Checkpoint"]), None)
441
+ # This will find nodes that aren't actively connected to anything, and skip loading lora's for them.
442
+ xy_lora_id = next((x for x in prompt if str(prompt[x]["class_type"]) == "easy XYInputs: Lora"), None)
443
+ if xy_lora_id is not None:
444
+ can_load_lora = False
445
+ if xy_model_id is not None:
446
+ node = prompt[xy_model_id]
447
+ if "ckpt_name_1" in node["inputs"]:
448
+ ckpt_name_1 = node["inputs"]["ckpt_name_1"]
449
+ model, clip, vae, clip_vision = self.load_checkpoint(ckpt_name_1)
450
+ can_load_lora = False
451
+ elif model_override is not None and clip_override is not None and vae_override is not None:
452
+ model = model_override
453
+ clip = clip_override
454
+ vae = vae_override
455
+ else:
456
+ model, clip, vae, clip_vision = self.load_checkpoint(ckpt_name, config_name)
457
+ if model_override is not None:
458
+ model = model_override
459
+ if vae_override is not None:
460
+ vae = vae_override
461
+ elif clip_override is not None:
462
+ clip = clip_override
463
+
464
+
465
+ if optional_lora_stack is not None and can_load_lora:
466
+ for lora in optional_lora_stack:
467
+ # This is a subtle bit of code because it uses the model created by the last call, and passes it to the next call.
468
+ lora = {"lora_name": lora[0], "model": model, "clip": clip, "model_strength": lora[1],
469
+ "clip_strength": lora[2]}
470
+ model, clip = self.load_lora(lora)
471
+ lora['model'] = model
472
+ lora['clip'] = clip
473
+ lora_stack.append(lora)
474
+
475
+ if lora_name != "None" and can_load_lora:
476
+ lora = {"lora_name": lora_name, "model": model, "clip": clip, "model_strength": lora_model_strength,
477
+ "clip_strength": lora_clip_strength}
478
+ model, clip = self.load_lora(lora)
479
+ lora_stack.append(lora)
480
+
481
+ # Check for custom VAE
482
+ if vae_name not in ["Baked VAE", "Baked-VAE"]:
483
+ vae = self.load_vae(vae_name)
484
+ # CLIP skip
485
+ if not clip:
486
+ raise Exception("No CLIP found")
487
+
488
+ return model, clip, vae, clip_vision, lora_stack
489
+
490
+ # Kolors
491
+ def load_kolors_unet(self, unet_name):
492
+ if unet_name in self.loaded_objects["unet"]:
493
+ log_node_info("Load Kolors UNet", f"{unet_name} cached")
494
+ return self.loaded_objects["unet"][unet_name][0]
495
+ else:
496
+ from ..modules.kolors.loader import applyKolorsUnet
497
+ with applyKolorsUnet():
498
+ unet_path = folder_paths.get_full_path("unet", unet_name)
499
+ sd = comfy.utils.load_torch_file(unet_path)
500
+ model = comfy.sd.load_unet_state_dict(sd)
501
+ if model is None:
502
+ raise RuntimeError("ERROR: Could not detect model type of: {}".format(unet_path))
503
+
504
+ self.add_to_cache("unet", unet_name, model)
505
+ self.eviction_based_on_memory()
506
+
507
+ return model
508
+
509
+ def load_chatglm3(self, chatglm3_name):
510
+ from ..modules.kolors.loader import load_chatglm3
511
+ if chatglm3_name in self.loaded_objects["chatglm3"]:
512
+ log_node_info("Load ChatGLM3", f"{chatglm3_name} cached")
513
+ return self.loaded_objects["chatglm3"][chatglm3_name][0]
514
+
515
+ chatglm_model = load_chatglm3(model_path=folder_paths.get_full_path("llm", chatglm3_name))
516
+ self.add_to_cache("chatglm3", chatglm3_name, chatglm_model)
517
+ self.eviction_based_on_memory()
518
+
519
+ return chatglm_model
520
+
521
+
522
+ # DiT
523
+ def load_dit_ckpt(self, ckpt_name, model_name, **kwargs):
524
+ if (ckpt_name+'_'+model_name) in self.loaded_objects["ckpt"]:
525
+ return self.loaded_objects["ckpt"][ckpt_name+'_'+model_name][0]
526
+ model = None
527
+ ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
528
+ model_type = kwargs['model_type'] if "model_type" in kwargs else 'PixArt'
529
+ if model_type == 'PixArt':
530
+ pixart_conf = kwargs['pixart_conf']
531
+ model_conf = pixart_conf[model_name]
532
+ model = load_pixart(ckpt_path, model_conf)
533
+ if model:
534
+ self.add_to_cache("ckpt", ckpt_name + '_' + model_name, model)
535
+ self.eviction_based_on_memory()
536
+ return model
537
+
538
+ def load_t5_from_sd3_clip(self, sd3_clip, padding):
539
+ try:
540
+ from comfy.text_encoders.sd3_clip import SD3Tokenizer, SD3ClipModel
541
+ except:
542
+ from comfy.sd3_clip import SD3Tokenizer, SD3ClipModel
543
+ import copy
544
+
545
+ clip = sd3_clip.clone()
546
+ assert clip.cond_stage_model.t5xxl is not None, "CLIP must have T5 loaded!"
547
+
548
+ # remove transformer
549
+ transformer = clip.cond_stage_model.t5xxl.transformer
550
+ clip.cond_stage_model.t5xxl.transformer = None
551
+
552
+ # clone object
553
+ tmp = SD3ClipModel(clip_l=False, clip_g=False, t5=False)
554
+ tmp.t5xxl = copy.deepcopy(clip.cond_stage_model.t5xxl)
555
+ # put transformer back
556
+ clip.cond_stage_model.t5xxl.transformer = transformer
557
+ tmp.t5xxl.transformer = transformer
558
+
559
+ # override special tokens
560
+ tmp.t5xxl.special_tokens = copy.deepcopy(clip.cond_stage_model.t5xxl.special_tokens)
561
+ tmp.t5xxl.special_tokens.pop("end") # make sure empty tokens match
562
+
563
+ # tokenizer
564
+ tok = SD3Tokenizer()
565
+ tok.t5xxl.min_length = padding
566
+
567
+ clip.cond_stage_model = tmp
568
+ clip.tokenizer = tok
569
+
570
+ return clip
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/log.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ COLORS_FG = {
2
+ 'BLACK': '\33[30m',
3
+ 'RED': '\33[31m',
4
+ 'GREEN': '\33[32m',
5
+ 'YELLOW': '\33[33m',
6
+ 'BLUE': '\33[34m',
7
+ 'MAGENTA': '\33[35m',
8
+ 'CYAN': '\33[36m',
9
+ 'WHITE': '\33[37m',
10
+ 'GREY': '\33[90m',
11
+ 'BRIGHT_RED': '\33[91m',
12
+ 'BRIGHT_GREEN': '\33[92m',
13
+ 'BRIGHT_YELLOW': '\33[93m',
14
+ 'BRIGHT_BLUE': '\33[94m',
15
+ 'BRIGHT_MAGENTA': '\33[95m',
16
+ 'BRIGHT_CYAN': '\33[96m',
17
+ 'BRIGHT_WHITE': '\33[97m',
18
+ }
19
+ COLORS_STYLE = {
20
+ 'RESET': '\33[0m',
21
+ 'BOLD': '\33[1m',
22
+ 'NORMAL': '\33[22m',
23
+ 'ITALIC': '\33[3m',
24
+ 'UNDERLINE': '\33[4m',
25
+ 'BLINK': '\33[5m',
26
+ 'BLINK2': '\33[6m',
27
+ 'SELECTED': '\33[7m',
28
+ }
29
+ COLORS_BG = {
30
+ 'BLACK': '\33[40m',
31
+ 'RED': '\33[41m',
32
+ 'GREEN': '\33[42m',
33
+ 'YELLOW': '\33[43m',
34
+ 'BLUE': '\33[44m',
35
+ 'MAGENTA': '\33[45m',
36
+ 'CYAN': '\33[46m',
37
+ 'WHITE': '\33[47m',
38
+ 'GREY': '\33[100m',
39
+ 'BRIGHT_RED': '\33[101m',
40
+ 'BRIGHT_GREEN': '\33[102m',
41
+ 'BRIGHT_YELLOW': '\33[103m',
42
+ 'BRIGHT_BLUE': '\33[104m',
43
+ 'BRIGHT_MAGENTA': '\33[105m',
44
+ 'BRIGHT_CYAN': '\33[106m',
45
+ 'BRIGHT_WHITE': '\33[107m',
46
+ }
47
+
48
+ def log_node_success(node_name, message=None):
49
+ """Logs a success message."""
50
+ _log_node(COLORS_FG["GREEN"], node_name, message)
51
+
52
+ def log_node_info(node_name, message=None):
53
+ """Logs an info message."""
54
+ _log_node(COLORS_FG["CYAN"], node_name, message)
55
+
56
+
57
+ def log_node_warn(node_name, message=None):
58
+ """Logs an warn message."""
59
+ _log_node(COLORS_FG["YELLOW"], node_name, message)
60
+
61
+ def log_node_error(node_name, message=None):
62
+ """Logs an warn message."""
63
+ _log_node(COLORS_FG["RED"], node_name, message)
64
+
65
+ def log_node(node_name, message=None):
66
+ """Logs a message."""
67
+ _log_node(COLORS_FG["CYAN"], node_name, message)
68
+
69
+
70
+ def _log_node(color, node_name, message=None, prefix=''):
71
+ print(_get_log_msg(color, node_name, message, prefix=prefix))
72
+
73
+ def _get_log_msg(color, node_name, message=None, prefix=''):
74
+ msg = f'{COLORS_STYLE["BOLD"]}{color}{prefix}[EasyUse] {node_name.replace(" (EasyUse)", "")}'
75
+ msg += f':{COLORS_STYLE["RESET"]} {message}' if message is not None else f'{COLORS_STYLE["RESET"]}'
76
+ return msg
77
+
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/math.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Math utility functions for formula evaluation
3
+ """
4
+ import math
5
+ import re
6
+
7
+ def evaluate_formula(formula: str, a=0, b=0, c=0, d=0):
8
+ """
9
+ 计算字符串数学公式
10
+
11
+ 支持的运算符和函数:
12
+ - 基本运算:+, -, *, /, //, %, **
13
+ - 比较运算:>, <, >=, <=, ==, !=
14
+ - 数学函数:abs, pow, round, ceil, floor, sqrt, exp, log, log10
15
+ - 三角函数:sin, cos, tan, asin, acos, atan
16
+ - 常量:pi, e
17
+
18
+ Args:
19
+ formula: 数学公式字符串,可以使用变量a、b、c、d
20
+ a: 变量a的值
21
+ b: 变量b的值
22
+ c: 变量c的值
23
+ d: 变量d的值
24
+
25
+ Returns:
26
+ 如果任意输入为list则返回list[float],否则返回float
27
+
28
+ Examples:
29
+ >>> evaluate_formula("a + b", 1, 2)
30
+ 3.0
31
+ >>> evaluate_formula("pow(a, 2)", 5)
32
+ 25.0
33
+ >>> evaluate_formula("ceil(a / b)", 5, 2)
34
+ 3.0
35
+ >>> evaluate_formula("(a>b)*b+(a<=b)*a", 5, 3)
36
+ 3.0
37
+ >>> evaluate_formula("(a>b)*b+(a<=b)*a", 2, 3)
38
+ 2.0
39
+ """
40
+ # 安全的数学函数白名单
41
+ safe_dict = {
42
+ # 基本运算
43
+ 'abs': abs,
44
+ 'pow': pow,
45
+ 'round': round,
46
+ # 数学函数
47
+ 'ceil': math.ceil,
48
+ 'floor': math.floor,
49
+ 'sqrt': math.sqrt,
50
+ 'exp': math.exp,
51
+ 'log': math.log,
52
+ 'log10': math.log10,
53
+ # 三角函数
54
+ 'sin': math.sin,
55
+ 'cos': math.cos,
56
+ 'tan': math.tan,
57
+ 'asin': math.asin,
58
+ 'acos': math.acos,
59
+ 'atan': math.atan,
60
+ # 常量
61
+ 'pi': math.pi,
62
+ 'e': math.e,
63
+ }
64
+
65
+ # 判断是否有 list 输入
66
+ list_inputs = {k: v for k, v in {'a': a, 'b': b, 'c': c, 'd': d}.items() if isinstance(v, (list, tuple))}
67
+ scalar_inputs = {k: v for k, v in {'a': a, 'b': b, 'c': c, 'd': d}.items() if not isinstance(v, (list, tuple))}
68
+
69
+ def _eval_single(vals: dict) -> float:
70
+ env = dict(safe_dict)
71
+ env.update({k: float(v) for k, v in vals.items()})
72
+ try:
73
+ result = eval(formula, {"__builtins__": {}}, env)
74
+ return float(result)
75
+ except Exception as e:
76
+ raise ValueError(f"公式计算错误: {str(e)}")
77
+
78
+ if not list_inputs:
79
+ # 全是标量
80
+ return _eval_single({k: v for k, v in {'a': a, 'b': b, 'c': c, 'd': d}.items()})
81
+
82
+ # 有 list 输入,逐元素计算
83
+ max_len = max(len(v) for v in list_inputs.values())
84
+ results = []
85
+ for i in range(max_len):
86
+ vals = {k: float(v) for k, v in scalar_inputs.items()}
87
+ for k, v in list_inputs.items():
88
+ vals[k] = float(v[i] if i < len(v) else v[-1])
89
+ results.append(_eval_single(vals))
90
+ return results
91
+
92
+
93
+ def ceil_value(value: float) -> int:
94
+ """向上取整"""
95
+ return math.ceil(value)
96
+
97
+
98
+ def floor_value(value: float) -> int:
99
+ """向下取整"""
100
+ return math.floor(value)
101
+
102
+
103
+ def round_value(value: float, decimals: int = 0) -> float:
104
+ """
105
+ 四舍五入
106
+
107
+ Args:
108
+ value: 要取整的值
109
+ decimals: 保留小数位数
110
+
111
+ Returns:
112
+ 四舍五入后的值
113
+ """
114
+ return round(value, decimals)
115
+
116
+
117
+ def power(base: float, exponent: float) -> float:
118
+ """计算幂运算"""
119
+ return math.pow(base, exponent)
120
+
121
+
122
+ def sqrt_value(value: float) -> float:
123
+ """计算平方根"""
124
+ if value < 0:
125
+ raise ValueError("不能对负数求平方根")
126
+ return math.sqrt(value)
127
+
128
+
129
+ def add(a: float, b: float) -> float:
130
+ """加法"""
131
+ return a + b
132
+
133
+
134
+ def subtract(a: float, b: float) -> float:
135
+ """减法"""
136
+ return a - b
137
+
138
+
139
+ def multiply(a: float, b: float) -> float:
140
+ """乘法"""
141
+ return a * b
142
+
143
+
144
+ def divide(a: float, b: float) -> float:
145
+ """除法"""
146
+ if b == 0:
147
+ raise ValueError("除数不能为零")
148
+ return a / b
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/messages.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from server import PromptServer
2
+ from aiohttp import web
3
+ import time
4
+ import json
5
+
6
+ class MessageCancelled(Exception):
7
+ pass
8
+
9
+ class Message:
10
+ stash = {}
11
+ messages = {}
12
+ cancelled = False
13
+
14
+ @classmethod
15
+ def addMessage(cls, id, message):
16
+ if message == '__cancel__':
17
+ cls.messages = {}
18
+ cls.cancelled = True
19
+ elif message == '__start__':
20
+ cls.messages = {}
21
+ cls.stash = {}
22
+ cls.cancelled = False
23
+ else:
24
+ cls.messages[str(id)] = message
25
+
26
+ @classmethod
27
+ def waitForMessage(cls, id, period=0.1, asList=False):
28
+ sid = str(id)
29
+ while not (sid in cls.messages) and not ("-1" in cls.messages):
30
+ if cls.cancelled:
31
+ cls.cancelled = False
32
+ raise MessageCancelled()
33
+ time.sleep(period)
34
+ if cls.cancelled:
35
+ cls.cancelled = False
36
+ raise MessageCancelled()
37
+ message = cls.messages.pop(str(id), None) or cls.messages.pop("-1")
38
+ try:
39
+ if asList:
40
+ return [str(x.strip()) for x in message.split(",")]
41
+ else:
42
+ try:
43
+ return json.loads(message)
44
+ except ValueError:
45
+ return message
46
+ except ValueError:
47
+ print( f"ERROR IN MESSAGE - failed to parse '${message}' as ${'comma separated list of strings' if asList else 'string'}")
48
+ return [message] if asList else message
49
+
50
+
51
+ @PromptServer.instance.routes.post('/easyuse/message_callback')
52
+ async def message_callback(request):
53
+ post = await request.post()
54
+ Message.addMessage(post.get("id"), post.get("message"))
55
+ return web.json_response({})
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/model.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import folder_paths
4
+ import server
5
+ from .utils import find_tags
6
+
7
+ class easyModelManager:
8
+
9
+ def __init__(self):
10
+ self.img_suffixes = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".tiff", ".svg", ".tif", ".tiff"]
11
+ self.default_suffixes = [".ckpt", ".pt", ".bin", ".pth", ".safetensors"]
12
+ self.models_config = {
13
+ "checkpoints": {"suffix": self.default_suffixes},
14
+ "loras": {"suffix": self.default_suffixes},
15
+ "unet": {"suffix": self.default_suffixes},
16
+ }
17
+ self.model_lists = {}
18
+
19
+ def find_thumbnail(self, model_type, name):
20
+ file_no_ext = os.path.splitext(name)[0]
21
+ for ext in self.img_suffixes:
22
+ full_path = folder_paths.get_full_path(model_type, file_no_ext + ext)
23
+ if os.path.isfile(str(full_path)):
24
+ return full_path
25
+ return None
26
+
27
+ def get_model_lists(self, model_type):
28
+ if model_type not in self.models_config:
29
+ return []
30
+ filenames = folder_paths.get_filename_list(model_type)
31
+ model_lists = []
32
+ for name in filenames:
33
+ model_suffix = os.path.splitext(name)[-1]
34
+ if model_suffix not in self.models_config[model_type]["suffix"]:
35
+ continue
36
+ else:
37
+ cfg = {
38
+ "name": os.path.basename(os.path.splitext(name)[0]),
39
+ "full_name": name,
40
+ "remark": '',
41
+ "file_path": folder_paths.get_full_path(model_type, name),
42
+ "type": model_type,
43
+ "suffix": model_suffix,
44
+ "dir_tags": find_tags(name),
45
+ "cover": self.find_thumbnail(model_type, name),
46
+ "metadata": None,
47
+ "sha256": None
48
+ }
49
+ model_lists.append(cfg)
50
+
51
+ return model_lists
52
+
53
+ def get_model_info(self, model_type, model_name):
54
+ pass
55
+
56
+ # if __name__ == "__main__":
57
+ # manager = easyModelManager()
58
+ # print(manager.get_model_lists("checkpoints"))
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/sampler.py ADDED
@@ -0,0 +1,1053 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import comfy
2
+ import comfy.model_management
3
+ import comfy.samplers
4
+ import torch
5
+ import numpy as np
6
+ import latent_preview
7
+ from nodes import MAX_RESOLUTION
8
+ from PIL import Image
9
+ from typing import Dict, List, Optional, Tuple, Union, Any
10
+ from ..modules.brushnet.model_patch import add_model_patch
11
+
12
+ class easySampler:
13
+ def __init__(self):
14
+ self.last_helds: dict[str, list] = {
15
+ "results": [],
16
+ "pipe_line": [],
17
+ }
18
+ self.device = comfy.model_management.intermediate_device()
19
+
20
+ @staticmethod
21
+ def tensor2pil(image: torch.Tensor) -> Image.Image:
22
+ """Convert a torch tensor to a PIL image."""
23
+ return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
24
+
25
+ @staticmethod
26
+ def pil2tensor(image: Image.Image) -> torch.Tensor:
27
+ """Convert a PIL image to a torch tensor."""
28
+ return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
29
+
30
+ @staticmethod
31
+ def enforce_mul_of_64(d):
32
+ d = int(d)
33
+ if d <= 7:
34
+ d = 8
35
+ leftover = d % 8 # 8 is the number of pixels per byte
36
+ if leftover != 0: # if the number of pixels is not a multiple of 8
37
+ if (leftover < 4): # if the number of pixels is less than 4
38
+ d -= leftover # remove the leftover pixels
39
+ else: # if the number of pixels is more than 4
40
+ d += 8 - leftover # add the leftover pixels
41
+
42
+ return int(d)
43
+
44
+ @staticmethod
45
+ def safe_split(to_split: str, delimiter: str) -> List[str]:
46
+ """Split the input string and return a list of non-empty parts."""
47
+ parts = to_split.split(delimiter)
48
+ parts = [part for part in parts if part not in ('', ' ', ' ')]
49
+
50
+ while len(parts) < 2:
51
+ parts.append('None')
52
+ return parts
53
+
54
+ def emptyLatent(self, resolution, empty_latent_width, empty_latent_height, batch_size=1, compression=0, model_type='sd', video_length=25):
55
+ if resolution not in ["自定义 x 自定义", 'width x height (custom)']:
56
+ try:
57
+ width, height = map(int, resolution.split(' x '))
58
+ empty_latent_width = width
59
+ empty_latent_height = height
60
+ except ValueError:
61
+ raise ValueError("Invalid base_resolution format.")
62
+ if model_type == 'sd3':
63
+ latent = torch.ones([batch_size, 16, empty_latent_height // 8, empty_latent_width // 8], device=self.device) * 0.0609
64
+ samples = {"samples": latent}
65
+ elif model_type == 'mochi':
66
+ latent = torch.zeros([batch_size, 12, ((video_length - 1) // 6) + 1, empty_latent_height // 8, empty_latent_width // 8], device=self.device)
67
+ samples = {"samples": latent}
68
+ elif compression == 0:
69
+ latent = torch.zeros([batch_size, 4, empty_latent_height // 8, empty_latent_width // 8], device=self.device)
70
+ samples = {"samples": latent}
71
+ else:
72
+ latent_c = torch.zeros(
73
+ [batch_size, 16, empty_latent_height // compression, empty_latent_width // compression])
74
+ latent_b = torch.zeros([batch_size, 4, empty_latent_height // 4, empty_latent_width // 4])
75
+
76
+ samples = ({"samples": latent_c}, {"samples": latent_b})
77
+ return samples
78
+
79
+ def prepare_noise(self, latent_image, seed, noise_inds=None, noise_device="cpu", incremental_seed_mode="comfy",
80
+ variation_seed=None, variation_strength=None):
81
+ """
82
+ creates random noise given a latent image and a seed.
83
+ optional arg skip can be used to skip and discard x number of noise generations for a given seed
84
+ """
85
+
86
+ latent_size = latent_image.size()
87
+ latent_size_1batch = [1, latent_size[1], latent_size[2], latent_size[3]]
88
+
89
+ if variation_strength is not None and variation_strength > 0 or incremental_seed_mode.startswith(
90
+ "variation str inc"):
91
+ if noise_device == "cpu":
92
+ variation_generator = torch.manual_seed(variation_seed)
93
+ else:
94
+ torch.cuda.manual_seed(variation_seed)
95
+ variation_generator = None
96
+
97
+ variation_latent = torch.randn(latent_size_1batch, dtype=latent_image.dtype, layout=latent_image.layout,
98
+ generator=variation_generator, device=noise_device)
99
+ else:
100
+ variation_latent = None
101
+
102
+ def apply_variation(input_latent, strength_up=None):
103
+ if variation_latent is None:
104
+ return input_latent
105
+ else:
106
+ strength = variation_strength
107
+
108
+ if strength_up is not None:
109
+ strength += strength_up
110
+
111
+ variation_noise = variation_latent.expand(input_latent.size()[0], -1, -1, -1)
112
+ result = (1 - strength) * input_latent + strength * variation_noise
113
+ return result
114
+
115
+ # method: incremental seed batch noise
116
+ if noise_inds is None and incremental_seed_mode == "incremental":
117
+ batch_cnt = latent_size[0]
118
+
119
+ latents = None
120
+ for i in range(batch_cnt):
121
+ if noise_device == "cpu":
122
+ generator = torch.manual_seed(seed + i)
123
+ else:
124
+ torch.cuda.manual_seed(seed + i)
125
+ generator = None
126
+
127
+ latent = torch.randn(latent_size_1batch, dtype=latent_image.dtype, layout=latent_image.layout,
128
+ generator=generator, device=noise_device)
129
+
130
+ latent = apply_variation(latent)
131
+
132
+ if latents is None:
133
+ latents = latent
134
+ else:
135
+ latents = torch.cat((latents, latent), dim=0)
136
+
137
+ return latents
138
+
139
+ # method: incremental variation batch noise
140
+ elif noise_inds is None and incremental_seed_mode.startswith("variation str inc"):
141
+ batch_cnt = latent_size[0]
142
+
143
+ latents = None
144
+ for i in range(batch_cnt):
145
+ if noise_device == "cpu":
146
+ generator = torch.manual_seed(seed)
147
+ else:
148
+ torch.cuda.manual_seed(seed)
149
+ generator = None
150
+
151
+ latent = torch.randn(latent_size_1batch, dtype=latent_image.dtype, layout=latent_image.layout,
152
+ generator=generator, device=noise_device)
153
+
154
+ step = float(incremental_seed_mode[18:])
155
+ latent = apply_variation(latent, step * i)
156
+
157
+ if latents is None:
158
+ latents = latent
159
+ else:
160
+ latents = torch.cat((latents, latent), dim=0)
161
+
162
+ return latents
163
+
164
+ # method: comfy batch noise
165
+ if noise_device == "cpu":
166
+ generator = torch.manual_seed(seed)
167
+ else:
168
+ torch.cuda.manual_seed(seed)
169
+ generator = None
170
+
171
+ if noise_inds is None:
172
+ latents = torch.randn(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout,
173
+ generator=generator, device=noise_device)
174
+ latents = apply_variation(latents)
175
+ return latents
176
+
177
+ unique_inds, inverse = np.unique(noise_inds, return_inverse=True)
178
+ noises = []
179
+ for i in range(unique_inds[-1] + 1):
180
+ noise = torch.randn([1] + list(latent_image.size())[1:], dtype=latent_image.dtype,
181
+ layout=latent_image.layout,
182
+ generator=generator, device=noise_device)
183
+ if i in unique_inds:
184
+ noises.append(noise)
185
+ noises = [noises[i] for i in inverse]
186
+ noises = torch.cat(noises, axis=0)
187
+ return noises
188
+
189
+ def common_ksampler(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent, denoise=1.0,
190
+ disable_noise=False, start_step=None, last_step=None, force_full_denoise=False,
191
+ preview_latent=True, disable_pbar=False, noise_device='CPU'):
192
+ device = comfy.model_management.get_torch_device()
193
+ noise_device = 'cpu' if noise_device == 'CPU' else device
194
+ latent_image = latent["samples"]
195
+ latent_image = comfy.sample.fix_empty_latent_channels(model, latent_image)
196
+
197
+ noise_mask = None
198
+ if "noise_mask" in latent:
199
+ noise_mask = latent["noise_mask"]
200
+
201
+ preview_format = "JPEG"
202
+ if preview_format not in ["JPEG", "PNG"]:
203
+ preview_format = "JPEG"
204
+
205
+ previewer = False
206
+
207
+ if preview_latent:
208
+ previewer = latent_preview.get_previewer(device, model.model.latent_format)
209
+
210
+ pbar = comfy.utils.ProgressBar(steps)
211
+
212
+ def callback(step, x0, x, total_steps):
213
+ preview_bytes = None
214
+ if previewer:
215
+ preview_bytes = previewer.decode_latent_to_preview_image(preview_format, x0)
216
+ pbar.update_absolute(step + 1, total_steps, preview_bytes)
217
+
218
+ if disable_noise:
219
+ noise = torch.zeros(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout,
220
+ device=noise_device)
221
+ else:
222
+ batch_inds = latent["batch_index"] if "batch_index" in latent else None
223
+ noise = self.prepare_noise(latent_image, seed, batch_inds, noise_device=noise_device)
224
+
225
+ #######################################################################################
226
+ # add model patch
227
+ # brushnet
228
+ add_model_patch(model)
229
+ #######################################################################################
230
+ samples = comfy.sample.sample(model, noise, steps, cfg, sampler_name, scheduler, positive, negative,
231
+ latent_image,
232
+ denoise=denoise, disable_noise=disable_noise, start_step=start_step,
233
+ last_step=last_step,
234
+ force_full_denoise=force_full_denoise, noise_mask=noise_mask,
235
+ callback=callback,
236
+ disable_pbar=disable_pbar, seed=seed)
237
+ out = latent.copy()
238
+ out["samples"] = samples
239
+ return out
240
+
241
+ def custom_ksampler(self, model, seed, steps, cfg, _sampler, sigmas, positive, negative, latent,
242
+ disable_noise=False, preview_latent=True, disable_pbar=False, noise_device='CPU'):
243
+
244
+ device = comfy.model_management.get_torch_device()
245
+ noise_device = 'cpu' if noise_device == 'CPU' else device
246
+
247
+ latent_image = latent["samples"]
248
+
249
+ if disable_noise:
250
+ noise = torch.zeros(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, device=noise_device)
251
+ else:
252
+ batch_inds = latent["batch_index"] if "batch_index" in latent else None
253
+ noise = self.prepare_noise(latent_image, seed, batch_inds, noise_device=noise_device)
254
+
255
+ noise_mask = None
256
+ if "noise_mask" in latent:
257
+ noise_mask = latent["noise_mask"]
258
+
259
+ preview_format = "JPEG"
260
+ if preview_format not in ["JPEG", "PNG"]:
261
+ preview_format = "JPEG"
262
+
263
+ previewer = False
264
+
265
+ if preview_latent:
266
+ previewer = latent_preview.get_previewer(device, model.model.latent_format)
267
+
268
+ pbar = comfy.utils.ProgressBar(steps)
269
+
270
+ def callback(step, x0, x, total_steps):
271
+ preview_bytes = None
272
+ if previewer:
273
+ preview_bytes = previewer.decode_latent_to_preview_image(preview_format, x0)
274
+ pbar.update_absolute(step + 1, total_steps, preview_bytes)
275
+
276
+ samples = comfy.samplers.sample(model, noise, positive, negative, cfg, device, _sampler, sigmas, latent_image=latent_image, model_options=model.model_options,
277
+ denoise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=seed)
278
+
279
+ out = latent.copy()
280
+ out["samples"] = samples
281
+ return out
282
+
283
+ def custom_advanced_ksampler(self, guider, sampler, sigmas, latent_image, add_noise='enable', seed=0, preview_latent=False):
284
+ latent = latent_image
285
+ latent_image = latent["samples"]
286
+ latent = latent.copy()
287
+ latent_image = comfy.sample.fix_empty_latent_channels(guider.model_patcher, latent_image)
288
+ latent["samples"] = latent_image
289
+
290
+ device = comfy.model_management.get_torch_device()
291
+ noise_device = device if add_noise == 'enable (GPU=A1111)' else 'cpu'
292
+
293
+ if add_noise == 'disable':
294
+ noise = torch.zeros(latent_image.shape, dtype=latent_image.dtype, layout=latent_image.layout, device="cpu")
295
+ else:
296
+ batch_inds = latent["batch_index"] if "batch_index" in latent else None
297
+ noise = self.prepare_noise(latent_image, seed, batch_inds, noise_device=noise_device)
298
+
299
+ noise_mask = None
300
+ if "noise_mask" in latent:
301
+ noise_mask = latent["noise_mask"]
302
+
303
+ x0_output = {}
304
+ previewer = False
305
+
306
+ model = guider.model_patcher
307
+ steps = sigmas.shape[-1] - 1
308
+ if preview_latent:
309
+ previewer = latent_preview.get_previewer(model.load_device, model.model.latent_format)
310
+
311
+ pbar = comfy.utils.ProgressBar(steps)
312
+
313
+ preview_format = "JPEG"
314
+ if preview_format not in ["JPEG", "PNG"]:
315
+ preview_format = "JPEG"
316
+ def callback(step, x0, x, total_steps):
317
+ if x0_output is not None:
318
+ x0_output["x0"] = x0
319
+
320
+ preview_bytes = None
321
+ if previewer:
322
+ preview_bytes = previewer.decode_latent_to_preview_image(preview_format, x0)
323
+ pbar.update_absolute(step + 1, total_steps, preview_bytes)
324
+
325
+ disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED
326
+ samples = guider.sample(noise, latent_image, sampler, sigmas, denoise_mask=noise_mask,
327
+ callback=callback, disable_pbar=disable_pbar, seed=seed)
328
+ samples = samples.to(comfy.model_management.intermediate_device())
329
+
330
+ out = latent.copy()
331
+ out["samples"] = samples
332
+ if "x0" in x0_output:
333
+ out_denoised = latent.copy()
334
+ out_denoised["samples"] = guider.model_patcher.model.process_latent_out(x0_output["x0"].cpu())
335
+ else:
336
+ out_denoised = out
337
+ return (out, out_denoised)
338
+
339
+ def get_value_by_id(self, key: str, my_unique_id: Any) -> Optional[Any]:
340
+ """Retrieve value by its associated ID."""
341
+ try:
342
+ for value, id_ in self.last_helds[key]:
343
+ if id_ == my_unique_id:
344
+ return value
345
+ except KeyError:
346
+ return None
347
+
348
+ def update_value_by_id(self, key: str, my_unique_id: Any, new_value: Any) -> Union[bool, None]:
349
+ """Update the value associated with a given ID. Return True if updated, False if appended, None if key doesn't exist."""
350
+ try:
351
+ for i, (value, id_) in enumerate(self.last_helds[key]):
352
+ if id_ == my_unique_id:
353
+ self.last_helds[key][i] = (new_value, id_)
354
+ return True
355
+ self.last_helds[key].append((new_value, my_unique_id))
356
+ return False
357
+ except KeyError:
358
+ return False
359
+
360
+ def upscale(self, samples, upscale_method, scale_by, crop):
361
+ s = samples.copy()
362
+ width = self.enforce_mul_of_64(round(samples["samples"].shape[3] * scale_by))
363
+ height = self.enforce_mul_of_64(round(samples["samples"].shape[2] * scale_by))
364
+
365
+ if (width > MAX_RESOLUTION):
366
+ width = MAX_RESOLUTION
367
+ if (height > MAX_RESOLUTION):
368
+ height = MAX_RESOLUTION
369
+
370
+ s["samples"] = comfy.utils.common_upscale(samples["samples"], width, height, upscale_method, crop)
371
+ return (s,)
372
+
373
+ def handle_upscale(self, samples: dict, upscale_method: str, factor: float, crop: bool) -> dict:
374
+ """Upscale the samples if the upscale_method is not set to 'None'."""
375
+ if upscale_method != "None":
376
+ samples = self.upscale(samples, upscale_method, factor, crop)[0]
377
+ return samples
378
+
379
+ def init_state(self, my_unique_id: Any, key: str, default: Any) -> Any:
380
+ """Initialize the state by either fetching the stored value or setting a default."""
381
+ value = self.get_value_by_id(key, my_unique_id)
382
+ if value is not None:
383
+ return value
384
+ return default
385
+
386
+ def get_output(self, pipe: dict,) -> Tuple:
387
+ """Return a tuple of various elements fetched from the input pipe dictionary."""
388
+ return (
389
+ pipe,
390
+ pipe.get("images"),
391
+ pipe.get("model"),
392
+ pipe.get("positive"),
393
+ pipe.get("negative"),
394
+ pipe.get("samples"),
395
+ pipe.get("vae"),
396
+ pipe.get("clip"),
397
+ pipe.get("seed"),
398
+ )
399
+
400
+ def get_output_sdxl(self, sdxl_pipe: dict) -> Tuple:
401
+ """Return a tuple of various elements fetched from the input sdxl_pipe dictionary."""
402
+ return (
403
+ sdxl_pipe,
404
+ sdxl_pipe.get("model"),
405
+ sdxl_pipe.get("positive"),
406
+ sdxl_pipe.get("negative"),
407
+ sdxl_pipe.get("vae"),
408
+ sdxl_pipe.get("refiner_model"),
409
+ sdxl_pipe.get("refiner_positive"),
410
+ sdxl_pipe.get("refiner_negative"),
411
+ sdxl_pipe.get("refiner_vae"),
412
+ sdxl_pipe.get("samples"),
413
+ sdxl_pipe.get("clip"),
414
+ sdxl_pipe.get("images"),
415
+ sdxl_pipe.get("seed")
416
+ )
417
+
418
+ def loglinear_interp(t_steps, num_steps):
419
+ """
420
+ Performs log-linear interpolation of a given array of decreasing numbers.
421
+ """
422
+ xs = np.linspace(0, 1, len(t_steps))
423
+ ys = np.log(t_steps[::-1])
424
+
425
+ new_xs = np.linspace(0, 1, num_steps)
426
+ new_ys = np.interp(new_xs, xs, ys)
427
+
428
+ interped_ys = np.exp(new_ys)[::-1].copy()
429
+ return interped_ys
430
+
431
+ class alignYourStepsScheduler:
432
+
433
+ NOISE_LEVELS = {
434
+ "SD1": [14.6146412293, 6.4745760956, 3.8636745985, 2.6946151520, 1.8841921177, 1.3943805092, 0.9642583904,
435
+ 0.6523686016, 0.3977456272, 0.1515232662, 0.0291671582],
436
+ "SDXL": [14.6146412293, 6.3184485287, 3.7681790315, 2.1811480769, 1.3405244945, 0.8620721141, 0.5550693289,
437
+ 0.3798540708, 0.2332364134, 0.1114188177, 0.0291671582],
438
+ "SVD": [700.00, 54.5, 15.886, 7.977, 4.248, 1.789, 0.981, 0.403, 0.173, 0.034, 0.002]}
439
+
440
+ def get_sigmas(self, model_type, steps, denoise):
441
+
442
+ total_steps = steps
443
+ if denoise < 1.0:
444
+ if denoise <= 0.0:
445
+ return (torch.FloatTensor([]),)
446
+ total_steps = round(steps * denoise)
447
+
448
+ sigmas = self.NOISE_LEVELS[model_type][:]
449
+ if (steps + 1) != len(sigmas):
450
+ sigmas = loglinear_interp(sigmas, steps + 1)
451
+
452
+ sigmas = sigmas[-(total_steps + 1):]
453
+ sigmas[-1] = 0
454
+ return (torch.FloatTensor(sigmas),)
455
+
456
+
457
+ class gitsScheduler:
458
+
459
+ NOISE_LEVELS = {
460
+ 0.80: [
461
+ [14.61464119, 7.49001646, 0.02916753],
462
+ [14.61464119, 11.54541874, 6.77309084, 0.02916753],
463
+ [14.61464119, 11.54541874, 7.49001646, 3.07277966, 0.02916753],
464
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 2.05039096, 0.02916753],
465
+ [14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 2.05039096, 0.02916753],
466
+ [14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 3.07277966, 1.56271636, 0.02916753],
467
+ [14.61464119, 12.96784878, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 3.07277966, 1.56271636,
468
+ 0.02916753],
469
+ [14.61464119, 13.76078796, 12.2308979, 10.90732002, 8.75849152, 7.49001646, 5.85520077, 3.07277966,
470
+ 1.56271636, 0.02916753],
471
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 8.75849152, 7.49001646, 5.85520077,
472
+ 3.07277966, 1.56271636, 0.02916753],
473
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646,
474
+ 5.85520077, 3.07277966, 1.56271636, 0.02916753],
475
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646,
476
+ 6.14220476, 4.86714602, 3.07277966, 1.56271636, 0.02916753],
477
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278,
478
+ 7.49001646, 6.14220476, 4.86714602, 3.07277966, 1.56271636, 0.02916753],
479
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.24142551,
480
+ 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.07277966, 1.56271636, 0.02916753],
481
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.24142551,
482
+ 8.75849152, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.07277966, 1.56271636, 0.02916753],
483
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.24142551,
484
+ 8.75849152, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.1956799, 1.98035145, 0.86115354, 0.02916753],
485
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547,
486
+ 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.1956799, 1.98035145, 0.86115354,
487
+ 0.02916753],
488
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547,
489
+ 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.07277966, 1.84880662,
490
+ 0.83188516, 0.02916753],
491
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547,
492
+ 9.24142551, 8.75849152, 8.30717278, 7.88507891, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.07277966,
493
+ 1.84880662, 0.83188516, 0.02916753],
494
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547,
495
+ 9.24142551, 8.75849152, 8.30717278, 7.88507891, 7.49001646, 6.77309084, 5.85520077, 4.86714602, 3.75677586,
496
+ 2.84484982, 1.78698075, 0.803307, 0.02916753],
497
+ ],
498
+ 0.85: [
499
+ [14.61464119, 7.49001646, 0.02916753],
500
+ [14.61464119, 7.49001646, 1.84880662, 0.02916753],
501
+ [14.61464119, 11.54541874, 6.77309084, 1.56271636, 0.02916753],
502
+ [14.61464119, 11.54541874, 7.11996698, 3.07277966, 1.24153244, 0.02916753],
503
+ [14.61464119, 11.54541874, 7.49001646, 5.09240818, 2.84484982, 0.95350921, 0.02916753],
504
+ [14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.09240818, 2.84484982, 0.95350921, 0.02916753],
505
+ [14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.58536053, 3.1956799, 1.84880662, 0.803307, 0.02916753],
506
+ [14.61464119, 12.96784878, 11.54541874, 8.75849152, 7.49001646, 5.58536053, 3.1956799, 1.84880662, 0.803307,
507
+ 0.02916753],
508
+ [14.61464119, 12.96784878, 11.54541874, 8.75849152, 7.49001646, 6.14220476, 4.65472794, 3.07277966,
509
+ 1.84880662, 0.803307, 0.02916753],
510
+ [14.61464119, 13.76078796, 12.2308979, 10.90732002, 8.75849152, 7.49001646, 6.14220476, 4.65472794,
511
+ 3.07277966, 1.84880662, 0.803307, 0.02916753],
512
+ [14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.14220476,
513
+ 4.65472794, 3.07277966, 1.84880662, 0.803307, 0.02916753],
514
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646,
515
+ 6.14220476, 4.65472794, 3.07277966, 1.84880662, 0.803307, 0.02916753],
516
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278,
517
+ 7.49001646, 6.14220476, 4.65472794, 3.07277966, 1.84880662, 0.803307, 0.02916753],
518
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278,
519
+ 7.49001646, 6.14220476, 4.86714602, 3.60512662, 2.6383388, 1.56271636, 0.72133851, 0.02916753],
520
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278,
521
+ 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455, 2.45070267, 1.56271636, 0.72133851,
522
+ 0.02916753],
523
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.75849152,
524
+ 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455, 2.45070267, 1.56271636, 0.72133851,
525
+ 0.02916753],
526
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.24142551,
527
+ 8.75849152, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455, 2.45070267, 1.56271636,
528
+ 0.72133851, 0.02916753],
529
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547,
530
+ 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455, 2.45070267,
531
+ 1.56271636, 0.72133851, 0.02916753],
532
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547,
533
+ 9.24142551, 8.75849152, 8.30717278, 7.88507891, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455,
534
+ 2.45070267, 1.56271636, 0.72133851, 0.02916753],
535
+ ],
536
+ 0.90: [
537
+ [14.61464119, 6.77309084, 0.02916753],
538
+ [14.61464119, 7.49001646, 1.56271636, 0.02916753],
539
+ [14.61464119, 7.49001646, 3.07277966, 0.95350921, 0.02916753],
540
+ [14.61464119, 7.49001646, 4.86714602, 2.54230714, 0.89115214, 0.02916753],
541
+ [14.61464119, 11.54541874, 7.49001646, 4.86714602, 2.54230714, 0.89115214, 0.02916753],
542
+ [14.61464119, 11.54541874, 7.49001646, 5.09240818, 3.07277966, 1.61558151, 0.69515091, 0.02916753],
543
+ [14.61464119, 12.2308979, 8.75849152, 7.11996698, 4.86714602, 3.07277966, 1.61558151, 0.69515091,
544
+ 0.02916753],
545
+ [14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 2.95596409, 1.61558151,
546
+ 0.69515091, 0.02916753],
547
+ [14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.19988537, 1.24153244,
548
+ 0.57119018, 0.02916753],
549
+ [14.61464119, 12.96784878, 10.90732002, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.1956799,
550
+ 2.19988537, 1.24153244, 0.57119018, 0.02916753],
551
+ [14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 5.85520077, 4.45427561,
552
+ 3.1956799, 2.19988537, 1.24153244, 0.57119018, 0.02916753],
553
+ [14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.86714602,
554
+ 3.75677586, 2.84484982, 1.84880662, 1.08895338, 0.52423614, 0.02916753],
555
+ [14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.14220476,
556
+ 4.86714602, 3.75677586, 2.84484982, 1.84880662, 1.08895338, 0.52423614, 0.02916753],
557
+ [14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.44769001,
558
+ 5.58536053, 4.45427561, 3.32507086, 2.45070267, 1.61558151, 0.95350921, 0.45573691, 0.02916753],
559
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646,
560
+ 6.44769001, 5.58536053, 4.45427561, 3.32507086, 2.45070267, 1.61558151, 0.95350921, 0.45573691,
561
+ 0.02916753],
562
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646,
563
+ 6.77309084, 5.85520077, 4.86714602, 3.91689563, 3.07277966, 2.27973175, 1.56271636, 0.95350921, 0.45573691,
564
+ 0.02916753],
565
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278,
566
+ 7.49001646, 6.77309084, 5.85520077, 4.86714602, 3.91689563, 3.07277966, 2.27973175, 1.56271636, 0.95350921,
567
+ 0.45573691, 0.02916753],
568
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.75849152,
569
+ 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.86714602, 3.91689563, 3.07277966, 2.27973175, 1.56271636,
570
+ 0.95350921, 0.45573691, 0.02916753],
571
+ [14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.75849152,
572
+ 8.30717278, 7.49001646, 6.77309084, 5.85520077, 5.09240818, 4.45427561, 3.60512662, 2.95596409, 2.19988537,
573
+ 1.51179266, 0.89115214, 0.43325692, 0.02916753],
574
+ ],
575
+ 0.95: [
576
+ [14.61464119, 6.77309084, 0.02916753],
577
+ [14.61464119, 6.77309084, 1.56271636, 0.02916753],
578
+ [14.61464119, 7.49001646, 2.84484982, 0.89115214, 0.02916753],
579
+ [14.61464119, 7.49001646, 4.86714602, 2.36326075, 0.803307, 0.02916753],
580
+ [14.61464119, 7.49001646, 4.86714602, 2.95596409, 1.56271636, 0.64427125, 0.02916753],
581
+ [14.61464119, 11.54541874, 7.49001646, 4.86714602, 2.95596409, 1.56271636, 0.64427125, 0.02916753],
582
+ [14.61464119, 11.54541874, 7.49001646, 4.86714602, 3.07277966, 1.91321158, 1.08895338, 0.50118381,
583
+ 0.02916753],
584
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.07277966, 1.91321158, 1.08895338,
585
+ 0.50118381, 0.02916753],
586
+ [14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.07277966, 1.91321158,
587
+ 1.08895338, 0.50118381, 0.02916753],
588
+ [14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.19988537, 1.41535246,
589
+ 0.803307, 0.38853383, 0.02916753],
590
+ [14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.46139455, 2.6383388, 1.84880662,
591
+ 1.24153244, 0.72133851, 0.34370604, 0.02916753],
592
+ [14.61464119, 12.96784878, 10.90732002, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.46139455,
593
+ 2.6383388, 1.84880662, 1.24153244, 0.72133851, 0.34370604, 0.02916753],
594
+ [14.61464119, 12.96784878, 10.90732002, 8.75849152, 7.49001646, 6.14220476, 4.86714602, 3.75677586,
595
+ 2.95596409, 2.19988537, 1.56271636, 1.05362725, 0.64427125, 0.32104823, 0.02916753],
596
+ [14.61464119, 12.96784878, 10.90732002, 8.75849152, 7.49001646, 6.44769001, 5.58536053, 4.65472794,
597
+ 3.60512662, 2.95596409, 2.19988537, 1.56271636, 1.05362725, 0.64427125, 0.32104823, 0.02916753],
598
+ [14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 6.44769001, 5.58536053,
599
+ 4.65472794, 3.60512662, 2.95596409, 2.19988537, 1.56271636, 1.05362725, 0.64427125, 0.32104823,
600
+ 0.02916753],
601
+ [14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 6.44769001, 5.58536053,
602
+ 4.65472794, 3.75677586, 3.07277966, 2.45070267, 1.78698075, 1.24153244, 0.83188516, 0.50118381, 0.22545385,
603
+ 0.02916753],
604
+ [14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 6.77309084, 5.85520077,
605
+ 5.09240818, 4.45427561, 3.60512662, 2.95596409, 2.36326075, 1.72759056, 1.24153244, 0.83188516, 0.50118381,
606
+ 0.22545385, 0.02916753],
607
+ [14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.77309084,
608
+ 5.85520077, 5.09240818, 4.45427561, 3.60512662, 2.95596409, 2.36326075, 1.72759056, 1.24153244, 0.83188516,
609
+ 0.50118381, 0.22545385, 0.02916753],
610
+ [14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.77309084,
611
+ 5.85520077, 5.09240818, 4.45427561, 3.75677586, 3.07277966, 2.45070267, 1.91321158, 1.46270394, 1.05362725,
612
+ 0.72133851, 0.43325692, 0.19894916, 0.02916753],
613
+ ],
614
+ 1.00: [
615
+ [14.61464119, 1.56271636, 0.02916753],
616
+ [14.61464119, 6.77309084, 0.95350921, 0.02916753],
617
+ [14.61464119, 6.77309084, 2.36326075, 0.803307, 0.02916753],
618
+ [14.61464119, 7.11996698, 3.07277966, 1.56271636, 0.59516323, 0.02916753],
619
+ [14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.41535246, 0.57119018, 0.02916753],
620
+ [14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.61558151, 0.86115354, 0.38853383, 0.02916753],
621
+ [14.61464119, 11.54541874, 7.49001646, 4.86714602, 2.84484982, 1.61558151, 0.86115354, 0.38853383,
622
+ 0.02916753],
623
+ [14.61464119, 11.54541874, 7.49001646, 4.86714602, 3.07277966, 1.98035145, 1.24153244, 0.72133851,
624
+ 0.34370604, 0.02916753],
625
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.07277966, 1.98035145, 1.24153244,
626
+ 0.72133851, 0.34370604, 0.02916753],
627
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.27973175, 1.51179266,
628
+ 0.95350921, 0.54755926, 0.25053367, 0.02916753],
629
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.36326075, 1.61558151,
630
+ 1.08895338, 0.72133851, 0.41087446, 0.17026083, 0.02916753],
631
+ [14.61464119, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.36326075,
632
+ 1.61558151, 1.08895338, 0.72133851, 0.41087446, 0.17026083, 0.02916753],
633
+ [14.61464119, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.84484982,
634
+ 2.12350607, 1.56271636, 1.08895338, 0.72133851, 0.41087446, 0.17026083, 0.02916753],
635
+ [14.61464119, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.84484982,
636
+ 2.19988537, 1.61558151, 1.162866, 0.803307, 0.50118381, 0.27464288, 0.09824532, 0.02916753],
637
+ [14.61464119, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.75677586, 3.07277966,
638
+ 2.45070267, 1.84880662, 1.36964464, 1.01931262, 0.72133851, 0.45573691, 0.25053367, 0.09824532,
639
+ 0.02916753],
640
+ [14.61464119, 11.54541874, 8.75849152, 7.49001646, 6.14220476, 5.09240818, 4.26497746, 3.46139455,
641
+ 2.84484982, 2.19988537, 1.67050016, 1.24153244, 0.92192322, 0.64427125, 0.43325692, 0.25053367, 0.09824532,
642
+ 0.02916753],
643
+ [14.61464119, 11.54541874, 8.75849152, 7.49001646, 6.14220476, 5.09240818, 4.26497746, 3.60512662,
644
+ 2.95596409, 2.45070267, 1.91321158, 1.51179266, 1.12534678, 0.83188516, 0.59516323, 0.38853383, 0.22545385,
645
+ 0.09824532, 0.02916753],
646
+ [14.61464119, 12.2308979, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 5.09240818, 4.26497746,
647
+ 3.60512662, 2.95596409, 2.45070267, 1.91321158, 1.51179266, 1.12534678, 0.83188516, 0.59516323, 0.38853383,
648
+ 0.22545385, 0.09824532, 0.02916753],
649
+ [14.61464119, 12.2308979, 9.24142551, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 5.09240818,
650
+ 4.26497746, 3.60512662, 2.95596409, 2.45070267, 1.91321158, 1.51179266, 1.12534678, 0.83188516, 0.59516323,
651
+ 0.38853383, 0.22545385, 0.09824532, 0.02916753],
652
+ ],
653
+ 1.05: [
654
+ [14.61464119, 0.95350921, 0.02916753],
655
+ [14.61464119, 6.77309084, 0.89115214, 0.02916753],
656
+ [14.61464119, 6.77309084, 2.05039096, 0.72133851, 0.02916753],
657
+ [14.61464119, 6.77309084, 2.84484982, 1.28281462, 0.52423614, 0.02916753],
658
+ [14.61464119, 6.77309084, 3.07277966, 1.61558151, 0.803307, 0.34370604, 0.02916753],
659
+ [14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.56271636, 0.803307, 0.34370604, 0.02916753],
660
+ [14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.61558151, 0.95350921, 0.52423614, 0.22545385,
661
+ 0.02916753],
662
+ [14.61464119, 7.49001646, 4.86714602, 3.07277966, 1.98035145, 1.24153244, 0.74807048, 0.41087446,
663
+ 0.17026083, 0.02916753],
664
+ [14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.27973175, 1.51179266, 0.95350921, 0.59516323, 0.34370604,
665
+ 0.13792117, 0.02916753],
666
+ [14.61464119, 7.49001646, 5.09240818, 3.46139455, 2.45070267, 1.61558151, 1.08895338, 0.72133851,
667
+ 0.45573691, 0.25053367, 0.09824532, 0.02916753],
668
+ [14.61464119, 11.54541874, 7.49001646, 5.09240818, 3.46139455, 2.45070267, 1.61558151, 1.08895338,
669
+ 0.72133851, 0.45573691, 0.25053367, 0.09824532, 0.02916753],
670
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.36326075, 1.61558151,
671
+ 1.08895338, 0.72133851, 0.45573691, 0.25053367, 0.09824532, 0.02916753],
672
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.45070267, 1.72759056,
673
+ 1.24153244, 0.86115354, 0.59516323, 0.38853383, 0.22545385, 0.09824532, 0.02916753],
674
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.84484982, 2.19988537,
675
+ 1.61558151, 1.162866, 0.83188516, 0.59516323, 0.38853383, 0.22545385, 0.09824532, 0.02916753],
676
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.84484982, 2.19988537,
677
+ 1.67050016, 1.28281462, 0.95350921, 0.72133851, 0.52423614, 0.34370604, 0.19894916, 0.09824532,
678
+ 0.02916753],
679
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.95596409, 2.36326075,
680
+ 1.84880662, 1.41535246, 1.08895338, 0.83188516, 0.61951244, 0.45573691, 0.32104823, 0.19894916, 0.09824532,
681
+ 0.02916753],
682
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.95596409, 2.45070267,
683
+ 1.91321158, 1.51179266, 1.20157266, 0.95350921, 0.74807048, 0.57119018, 0.43325692, 0.29807833, 0.19894916,
684
+ 0.09824532, 0.02916753],
685
+ [14.61464119, 11.54541874, 8.30717278, 7.11996698, 5.85520077, 4.65472794, 3.60512662, 2.95596409,
686
+ 2.45070267, 1.91321158, 1.51179266, 1.20157266, 0.95350921, 0.74807048, 0.57119018, 0.43325692, 0.29807833,
687
+ 0.19894916, 0.09824532, 0.02916753],
688
+ [14.61464119, 11.54541874, 8.30717278, 7.11996698, 5.85520077, 4.65472794, 3.60512662, 2.95596409,
689
+ 2.45070267, 1.98035145, 1.61558151, 1.32549286, 1.08895338, 0.86115354, 0.69515091, 0.54755926, 0.41087446,
690
+ 0.29807833, 0.19894916, 0.09824532, 0.02916753],
691
+ ],
692
+ 1.10: [
693
+ [14.61464119, 0.89115214, 0.02916753],
694
+ [14.61464119, 2.36326075, 0.72133851, 0.02916753],
695
+ [14.61464119, 5.85520077, 1.61558151, 0.57119018, 0.02916753],
696
+ [14.61464119, 6.77309084, 2.45070267, 1.08895338, 0.45573691, 0.02916753],
697
+ [14.61464119, 6.77309084, 2.95596409, 1.56271636, 0.803307, 0.34370604, 0.02916753],
698
+ [14.61464119, 6.77309084, 3.07277966, 1.61558151, 0.89115214, 0.4783645, 0.19894916, 0.02916753],
699
+ [14.61464119, 6.77309084, 3.07277966, 1.84880662, 1.08895338, 0.64427125, 0.34370604, 0.13792117,
700
+ 0.02916753],
701
+ [14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.61558151, 0.95350921, 0.54755926, 0.27464288,
702
+ 0.09824532, 0.02916753],
703
+ [14.61464119, 7.49001646, 4.86714602, 2.95596409, 1.91321158, 1.24153244, 0.803307, 0.4783645, 0.25053367,
704
+ 0.09824532, 0.02916753],
705
+ [14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.05039096, 1.41535246, 0.95350921, 0.64427125,
706
+ 0.41087446, 0.22545385, 0.09824532, 0.02916753],
707
+ [14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.27973175, 1.61558151, 1.12534678, 0.803307, 0.54755926,
708
+ 0.36617002, 0.22545385, 0.09824532, 0.02916753],
709
+ [14.61464119, 7.49001646, 4.86714602, 3.32507086, 2.45070267, 1.72759056, 1.24153244, 0.89115214,
710
+ 0.64427125, 0.45573691, 0.32104823, 0.19894916, 0.09824532, 0.02916753],
711
+ [14.61464119, 7.49001646, 5.09240818, 3.60512662, 2.84484982, 2.05039096, 1.51179266, 1.08895338, 0.803307,
712
+ 0.59516323, 0.43325692, 0.29807833, 0.19894916, 0.09824532, 0.02916753],
713
+ [14.61464119, 7.49001646, 5.09240818, 3.60512662, 2.84484982, 2.12350607, 1.61558151, 1.24153244,
714
+ 0.95350921, 0.72133851, 0.54755926, 0.41087446, 0.29807833, 0.19894916, 0.09824532, 0.02916753],
715
+ [14.61464119, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.45070267, 1.84880662, 1.41535246, 1.08895338,
716
+ 0.83188516, 0.64427125, 0.50118381, 0.36617002, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
717
+ [14.61464119, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.45070267, 1.91321158, 1.51179266, 1.20157266,
718
+ 0.95350921, 0.74807048, 0.59516323, 0.45573691, 0.34370604, 0.25053367, 0.17026083, 0.09824532,
719
+ 0.02916753],
720
+ [14.61464119, 7.49001646, 5.85520077, 4.45427561, 3.46139455, 2.84484982, 2.19988537, 1.72759056,
721
+ 1.36964464, 1.08895338, 0.86115354, 0.69515091, 0.54755926, 0.43325692, 0.34370604, 0.25053367, 0.17026083,
722
+ 0.09824532, 0.02916753],
723
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.46139455, 2.84484982, 2.19988537,
724
+ 1.72759056, 1.36964464, 1.08895338, 0.86115354, 0.69515091, 0.54755926, 0.43325692, 0.34370604, 0.25053367,
725
+ 0.17026083, 0.09824532, 0.02916753],
726
+ [14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.46139455, 2.84484982, 2.19988537,
727
+ 1.72759056, 1.36964464, 1.08895338, 0.89115214, 0.72133851, 0.59516323, 0.4783645, 0.38853383, 0.29807833,
728
+ 0.22545385, 0.17026083, 0.09824532, 0.02916753],
729
+ ],
730
+ 1.15: [
731
+ [14.61464119, 0.83188516, 0.02916753],
732
+ [14.61464119, 1.84880662, 0.59516323, 0.02916753],
733
+ [14.61464119, 5.85520077, 1.56271636, 0.52423614, 0.02916753],
734
+ [14.61464119, 5.85520077, 1.91321158, 0.83188516, 0.34370604, 0.02916753],
735
+ [14.61464119, 5.85520077, 2.45070267, 1.24153244, 0.59516323, 0.25053367, 0.02916753],
736
+ [14.61464119, 5.85520077, 2.84484982, 1.51179266, 0.803307, 0.41087446, 0.17026083, 0.02916753],
737
+ [14.61464119, 5.85520077, 2.84484982, 1.56271636, 0.89115214, 0.50118381, 0.25053367, 0.09824532,
738
+ 0.02916753],
739
+ [14.61464119, 6.77309084, 3.07277966, 1.84880662, 1.12534678, 0.72133851, 0.43325692, 0.22545385,
740
+ 0.09824532, 0.02916753],
741
+ [14.61464119, 6.77309084, 3.07277966, 1.91321158, 1.24153244, 0.803307, 0.52423614, 0.34370604, 0.19894916,
742
+ 0.09824532, 0.02916753],
743
+ [14.61464119, 7.49001646, 4.86714602, 2.95596409, 1.91321158, 1.24153244, 0.803307, 0.52423614, 0.34370604,
744
+ 0.19894916, 0.09824532, 0.02916753],
745
+ [14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.05039096, 1.36964464, 0.95350921, 0.69515091, 0.4783645,
746
+ 0.32104823, 0.19894916, 0.09824532, 0.02916753],
747
+ [14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.803307, 0.59516323,
748
+ 0.43325692, 0.29807833, 0.19894916, 0.09824532, 0.02916753],
749
+ [14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.803307, 0.59516323,
750
+ 0.45573691, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
751
+ [14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.19988537, 1.61558151, 1.24153244, 0.95350921,
752
+ 0.74807048, 0.59516323, 0.45573691, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
753
+ [14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.78698075, 1.32549286, 1.01931262, 0.803307,
754
+ 0.64427125, 0.50118381, 0.38853383, 0.29807833, 0.22545385, 0.17026083, 0.09824532, 0.02916753],
755
+ [14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.78698075, 1.32549286, 1.01931262, 0.803307,
756
+ 0.64427125, 0.52423614, 0.41087446, 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532,
757
+ 0.02916753],
758
+ [14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.84880662, 1.41535246, 1.12534678, 0.89115214,
759
+ 0.72133851, 0.59516323, 0.4783645, 0.38853383, 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532,
760
+ 0.02916753],
761
+ [14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.84880662, 1.41535246, 1.12534678, 0.89115214,
762
+ 0.72133851, 0.59516323, 0.50118381, 0.41087446, 0.34370604, 0.27464288, 0.22545385, 0.17026083, 0.13792117,
763
+ 0.09824532, 0.02916753],
764
+ [14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.84880662, 1.41535246, 1.12534678, 0.89115214,
765
+ 0.72133851, 0.59516323, 0.50118381, 0.41087446, 0.34370604, 0.29807833, 0.25053367, 0.19894916, 0.17026083,
766
+ 0.13792117, 0.09824532, 0.02916753],
767
+ ],
768
+ 1.20: [
769
+ [14.61464119, 0.803307, 0.02916753],
770
+ [14.61464119, 1.56271636, 0.52423614, 0.02916753],
771
+ [14.61464119, 2.36326075, 0.92192322, 0.36617002, 0.02916753],
772
+ [14.61464119, 2.84484982, 1.24153244, 0.59516323, 0.25053367, 0.02916753],
773
+ [14.61464119, 5.85520077, 2.05039096, 0.95350921, 0.45573691, 0.17026083, 0.02916753],
774
+ [14.61464119, 5.85520077, 2.45070267, 1.24153244, 0.64427125, 0.29807833, 0.09824532, 0.02916753],
775
+ [14.61464119, 5.85520077, 2.45070267, 1.36964464, 0.803307, 0.45573691, 0.25053367, 0.09824532, 0.02916753],
776
+ [14.61464119, 5.85520077, 2.84484982, 1.61558151, 0.95350921, 0.59516323, 0.36617002, 0.19894916,
777
+ 0.09824532, 0.02916753],
778
+ [14.61464119, 5.85520077, 2.84484982, 1.67050016, 1.08895338, 0.74807048, 0.50118381, 0.32104823,
779
+ 0.19894916, 0.09824532, 0.02916753],
780
+ [14.61464119, 5.85520077, 2.95596409, 1.84880662, 1.24153244, 0.83188516, 0.59516323, 0.41087446,
781
+ 0.27464288, 0.17026083, 0.09824532, 0.02916753],
782
+ [14.61464119, 5.85520077, 3.07277966, 1.98035145, 1.36964464, 0.95350921, 0.69515091, 0.50118381,
783
+ 0.36617002, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
784
+ [14.61464119, 6.77309084, 3.46139455, 2.36326075, 1.56271636, 1.08895338, 0.803307, 0.59516323, 0.45573691,
785
+ 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
786
+ [14.61464119, 6.77309084, 3.46139455, 2.45070267, 1.61558151, 1.162866, 0.86115354, 0.64427125, 0.50118381,
787
+ 0.38853383, 0.29807833, 0.22545385, 0.17026083, 0.09824532, 0.02916753],
788
+ [14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.83188516,
789
+ 0.64427125, 0.50118381, 0.38853383, 0.29807833, 0.22545385, 0.17026083, 0.09824532, 0.02916753],
790
+ [14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.83188516,
791
+ 0.64427125, 0.50118381, 0.41087446, 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532,
792
+ 0.02916753],
793
+ [14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.83188516,
794
+ 0.64427125, 0.50118381, 0.41087446, 0.34370604, 0.27464288, 0.22545385, 0.17026083, 0.13792117, 0.09824532,
795
+ 0.02916753],
796
+ [14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.19988537, 1.61558151, 1.20157266, 0.92192322,
797
+ 0.72133851, 0.57119018, 0.45573691, 0.36617002, 0.29807833, 0.25053367, 0.19894916, 0.17026083, 0.13792117,
798
+ 0.09824532, 0.02916753],
799
+ [14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.19988537, 1.61558151, 1.24153244, 0.95350921,
800
+ 0.74807048, 0.59516323, 0.4783645, 0.38853383, 0.32104823, 0.27464288, 0.22545385, 0.19894916, 0.17026083,
801
+ 0.13792117, 0.09824532, 0.02916753],
802
+ [14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.19988537, 1.61558151, 1.24153244, 0.95350921,
803
+ 0.74807048, 0.59516323, 0.50118381, 0.41087446, 0.34370604, 0.29807833, 0.25053367, 0.22545385, 0.19894916,
804
+ 0.17026083, 0.13792117, 0.09824532, 0.02916753],
805
+ ],
806
+ 1.25: [
807
+ [14.61464119, 0.72133851, 0.02916753],
808
+ [14.61464119, 1.56271636, 0.50118381, 0.02916753],
809
+ [14.61464119, 2.05039096, 0.803307, 0.32104823, 0.02916753],
810
+ [14.61464119, 2.36326075, 0.95350921, 0.43325692, 0.17026083, 0.02916753],
811
+ [14.61464119, 2.84484982, 1.24153244, 0.59516323, 0.27464288, 0.09824532, 0.02916753],
812
+ [14.61464119, 3.07277966, 1.51179266, 0.803307, 0.43325692, 0.22545385, 0.09824532, 0.02916753],
813
+ [14.61464119, 5.85520077, 2.36326075, 1.24153244, 0.72133851, 0.41087446, 0.22545385, 0.09824532,
814
+ 0.02916753],
815
+ [14.61464119, 5.85520077, 2.45070267, 1.36964464, 0.83188516, 0.52423614, 0.34370604, 0.19894916,
816
+ 0.09824532, 0.02916753],
817
+ [14.61464119, 5.85520077, 2.84484982, 1.61558151, 0.98595673, 0.64427125, 0.43325692, 0.27464288,
818
+ 0.17026083, 0.09824532, 0.02916753],
819
+ [14.61464119, 5.85520077, 2.84484982, 1.67050016, 1.08895338, 0.74807048, 0.52423614, 0.36617002,
820
+ 0.25053367, 0.17026083, 0.09824532, 0.02916753],
821
+ [14.61464119, 5.85520077, 2.84484982, 1.72759056, 1.162866, 0.803307, 0.59516323, 0.45573691, 0.34370604,
822
+ 0.25053367, 0.17026083, 0.09824532, 0.02916753],
823
+ [14.61464119, 5.85520077, 2.95596409, 1.84880662, 1.24153244, 0.86115354, 0.64427125, 0.4783645, 0.36617002,
824
+ 0.27464288, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
825
+ [14.61464119, 5.85520077, 2.95596409, 1.84880662, 1.28281462, 0.92192322, 0.69515091, 0.52423614,
826
+ 0.41087446, 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
827
+ [14.61464119, 5.85520077, 2.95596409, 1.91321158, 1.32549286, 0.95350921, 0.72133851, 0.54755926,
828
+ 0.43325692, 0.34370604, 0.27464288, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
829
+ [14.61464119, 5.85520077, 2.95596409, 1.91321158, 1.32549286, 0.95350921, 0.72133851, 0.57119018,
830
+ 0.45573691, 0.36617002, 0.29807833, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
831
+ 0.02916753],
832
+ [14.61464119, 5.85520077, 2.95596409, 1.91321158, 1.32549286, 0.95350921, 0.74807048, 0.59516323, 0.4783645,
833
+ 0.38853383, 0.32104823, 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
834
+ 0.02916753],
835
+ [14.61464119, 5.85520077, 3.07277966, 2.05039096, 1.41535246, 1.05362725, 0.803307, 0.61951244, 0.50118381,
836
+ 0.41087446, 0.34370604, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
837
+ 0.02916753],
838
+ [14.61464119, 5.85520077, 3.07277966, 2.05039096, 1.41535246, 1.05362725, 0.803307, 0.64427125, 0.52423614,
839
+ 0.43325692, 0.36617002, 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117,
840
+ 0.09824532, 0.02916753],
841
+ [14.61464119, 5.85520077, 3.07277966, 2.05039096, 1.46270394, 1.08895338, 0.83188516, 0.66947293,
842
+ 0.54755926, 0.45573691, 0.38853383, 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916,
843
+ 0.17026083, 0.13792117, 0.09824532, 0.02916753],
844
+ ],
845
+ 1.30: [
846
+ [14.61464119, 0.72133851, 0.02916753],
847
+ [14.61464119, 1.24153244, 0.43325692, 0.02916753],
848
+ [14.61464119, 1.56271636, 0.59516323, 0.22545385, 0.02916753],
849
+ [14.61464119, 1.84880662, 0.803307, 0.36617002, 0.13792117, 0.02916753],
850
+ [14.61464119, 2.36326075, 1.01931262, 0.52423614, 0.25053367, 0.09824532, 0.02916753],
851
+ [14.61464119, 2.84484982, 1.36964464, 0.74807048, 0.41087446, 0.22545385, 0.09824532, 0.02916753],
852
+ [14.61464119, 3.07277966, 1.56271636, 0.89115214, 0.54755926, 0.34370604, 0.19894916, 0.09824532,
853
+ 0.02916753],
854
+ [14.61464119, 3.07277966, 1.61558151, 0.95350921, 0.61951244, 0.41087446, 0.27464288, 0.17026083,
855
+ 0.09824532, 0.02916753],
856
+ [14.61464119, 5.85520077, 2.45070267, 1.36964464, 0.83188516, 0.54755926, 0.36617002, 0.25053367,
857
+ 0.17026083, 0.09824532, 0.02916753],
858
+ [14.61464119, 5.85520077, 2.45070267, 1.41535246, 0.92192322, 0.64427125, 0.45573691, 0.34370604,
859
+ 0.25053367, 0.17026083, 0.09824532, 0.02916753],
860
+ [14.61464119, 5.85520077, 2.6383388, 1.56271636, 1.01931262, 0.72133851, 0.50118381, 0.36617002, 0.27464288,
861
+ 0.19894916, 0.13792117, 0.09824532, 0.02916753],
862
+ [14.61464119, 5.85520077, 2.84484982, 1.61558151, 1.05362725, 0.74807048, 0.54755926, 0.41087446,
863
+ 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
864
+ [14.61464119, 5.85520077, 2.84484982, 1.61558151, 1.08895338, 0.77538133, 0.57119018, 0.43325692,
865
+ 0.34370604, 0.27464288, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
866
+ [14.61464119, 5.85520077, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.59516323, 0.45573691, 0.36617002,
867
+ 0.29807833, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
868
+ [14.61464119, 5.85520077, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.59516323, 0.4783645, 0.38853383,
869
+ 0.32104823, 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
870
+ [14.61464119, 5.85520077, 2.84484982, 1.72759056, 1.162866, 0.83188516, 0.64427125, 0.50118381, 0.41087446,
871
+ 0.34370604, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
872
+ 0.02916753],
873
+ [14.61464119, 5.85520077, 2.84484982, 1.72759056, 1.162866, 0.83188516, 0.64427125, 0.52423614, 0.43325692,
874
+ 0.36617002, 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
875
+ 0.02916753],
876
+ [14.61464119, 5.85520077, 2.84484982, 1.78698075, 1.24153244, 0.92192322, 0.72133851, 0.57119018,
877
+ 0.45573691, 0.38853383, 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083,
878
+ 0.13792117, 0.09824532, 0.02916753],
879
+ [14.61464119, 5.85520077, 2.84484982, 1.78698075, 1.24153244, 0.92192322, 0.72133851, 0.57119018, 0.4783645,
880
+ 0.41087446, 0.36617002, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083,
881
+ 0.13792117, 0.09824532, 0.02916753],
882
+ ],
883
+ 1.35: [
884
+ [14.61464119, 0.69515091, 0.02916753],
885
+ [14.61464119, 0.95350921, 0.34370604, 0.02916753],
886
+ [14.61464119, 1.56271636, 0.57119018, 0.19894916, 0.02916753],
887
+ [14.61464119, 1.61558151, 0.69515091, 0.29807833, 0.09824532, 0.02916753],
888
+ [14.61464119, 1.84880662, 0.83188516, 0.43325692, 0.22545385, 0.09824532, 0.02916753],
889
+ [14.61464119, 2.45070267, 1.162866, 0.64427125, 0.36617002, 0.19894916, 0.09824532, 0.02916753],
890
+ [14.61464119, 2.84484982, 1.36964464, 0.803307, 0.50118381, 0.32104823, 0.19894916, 0.09824532, 0.02916753],
891
+ [14.61464119, 2.84484982, 1.41535246, 0.83188516, 0.54755926, 0.36617002, 0.25053367, 0.17026083,
892
+ 0.09824532, 0.02916753],
893
+ [14.61464119, 2.84484982, 1.56271636, 0.95350921, 0.64427125, 0.45573691, 0.32104823, 0.22545385,
894
+ 0.17026083, 0.09824532, 0.02916753],
895
+ [14.61464119, 2.84484982, 1.56271636, 0.95350921, 0.64427125, 0.45573691, 0.34370604, 0.25053367,
896
+ 0.19894916, 0.13792117, 0.09824532, 0.02916753],
897
+ [14.61464119, 3.07277966, 1.61558151, 1.01931262, 0.72133851, 0.52423614, 0.38853383, 0.29807833,
898
+ 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
899
+ [14.61464119, 3.07277966, 1.61558151, 1.01931262, 0.72133851, 0.52423614, 0.41087446, 0.32104823,
900
+ 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
901
+ [14.61464119, 3.07277966, 1.61558151, 1.05362725, 0.74807048, 0.54755926, 0.43325692, 0.34370604,
902
+ 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
903
+ [14.61464119, 3.07277966, 1.72759056, 1.12534678, 0.803307, 0.59516323, 0.45573691, 0.36617002, 0.29807833,
904
+ 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
905
+ [14.61464119, 3.07277966, 1.72759056, 1.12534678, 0.803307, 0.59516323, 0.4783645, 0.38853383, 0.32104823,
906
+ 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
907
+ [14.61464119, 5.85520077, 2.45070267, 1.51179266, 1.01931262, 0.74807048, 0.57119018, 0.45573691,
908
+ 0.36617002, 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
909
+ 0.02916753],
910
+ [14.61464119, 5.85520077, 2.6383388, 1.61558151, 1.08895338, 0.803307, 0.61951244, 0.50118381, 0.41087446,
911
+ 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
912
+ 0.02916753],
913
+ [14.61464119, 5.85520077, 2.6383388, 1.61558151, 1.08895338, 0.803307, 0.64427125, 0.52423614, 0.43325692,
914
+ 0.36617002, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117,
915
+ 0.09824532, 0.02916753],
916
+ [14.61464119, 5.85520077, 2.6383388, 1.61558151, 1.08895338, 0.803307, 0.64427125, 0.52423614, 0.45573691,
917
+ 0.38853383, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083,
918
+ 0.13792117, 0.09824532, 0.02916753],
919
+ ],
920
+ 1.40: [
921
+ [14.61464119, 0.59516323, 0.02916753],
922
+ [14.61464119, 0.95350921, 0.34370604, 0.02916753],
923
+ [14.61464119, 1.08895338, 0.43325692, 0.13792117, 0.02916753],
924
+ [14.61464119, 1.56271636, 0.64427125, 0.27464288, 0.09824532, 0.02916753],
925
+ [14.61464119, 1.61558151, 0.803307, 0.43325692, 0.22545385, 0.09824532, 0.02916753],
926
+ [14.61464119, 2.05039096, 0.95350921, 0.54755926, 0.34370604, 0.19894916, 0.09824532, 0.02916753],
927
+ [14.61464119, 2.45070267, 1.24153244, 0.72133851, 0.43325692, 0.27464288, 0.17026083, 0.09824532,
928
+ 0.02916753],
929
+ [14.61464119, 2.45070267, 1.24153244, 0.74807048, 0.50118381, 0.34370604, 0.25053367, 0.17026083,
930
+ 0.09824532, 0.02916753],
931
+ [14.61464119, 2.45070267, 1.28281462, 0.803307, 0.52423614, 0.36617002, 0.27464288, 0.19894916, 0.13792117,
932
+ 0.09824532, 0.02916753],
933
+ [14.61464119, 2.45070267, 1.28281462, 0.803307, 0.54755926, 0.38853383, 0.29807833, 0.22545385, 0.17026083,
934
+ 0.13792117, 0.09824532, 0.02916753],
935
+ [14.61464119, 2.84484982, 1.41535246, 0.86115354, 0.59516323, 0.43325692, 0.32104823, 0.25053367,
936
+ 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
937
+ [14.61464119, 2.84484982, 1.51179266, 0.95350921, 0.64427125, 0.45573691, 0.34370604, 0.27464288,
938
+ 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
939
+ [14.61464119, 2.84484982, 1.51179266, 0.95350921, 0.64427125, 0.4783645, 0.36617002, 0.29807833, 0.25053367,
940
+ 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
941
+ [14.61464119, 2.84484982, 1.56271636, 0.98595673, 0.69515091, 0.52423614, 0.41087446, 0.34370604,
942
+ 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
943
+ [14.61464119, 2.84484982, 1.56271636, 1.01931262, 0.72133851, 0.54755926, 0.43325692, 0.36617002,
944
+ 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
945
+ 0.02916753],
946
+ [14.61464119, 2.84484982, 1.61558151, 1.05362725, 0.74807048, 0.57119018, 0.45573691, 0.38853383,
947
+ 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
948
+ 0.02916753],
949
+ [14.61464119, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.61951244, 0.50118381, 0.41087446, 0.36617002,
950
+ 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
951
+ 0.02916753],
952
+ [14.61464119, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.61951244, 0.50118381, 0.43325692, 0.38853383,
953
+ 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117,
954
+ 0.09824532, 0.02916753],
955
+ [14.61464119, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.64427125, 0.52423614, 0.45573691, 0.41087446,
956
+ 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083,
957
+ 0.13792117, 0.09824532, 0.02916753],
958
+ ],
959
+ 1.45: [
960
+ [14.61464119, 0.59516323, 0.02916753],
961
+ [14.61464119, 0.803307, 0.25053367, 0.02916753],
962
+ [14.61464119, 0.95350921, 0.34370604, 0.09824532, 0.02916753],
963
+ [14.61464119, 1.24153244, 0.54755926, 0.25053367, 0.09824532, 0.02916753],
964
+ [14.61464119, 1.56271636, 0.72133851, 0.36617002, 0.19894916, 0.09824532, 0.02916753],
965
+ [14.61464119, 1.61558151, 0.803307, 0.45573691, 0.27464288, 0.17026083, 0.09824532, 0.02916753],
966
+ [14.61464119, 1.91321158, 0.95350921, 0.57119018, 0.36617002, 0.25053367, 0.17026083, 0.09824532,
967
+ 0.02916753],
968
+ [14.61464119, 2.19988537, 1.08895338, 0.64427125, 0.41087446, 0.27464288, 0.19894916, 0.13792117,
969
+ 0.09824532, 0.02916753],
970
+ [14.61464119, 2.45070267, 1.24153244, 0.74807048, 0.50118381, 0.34370604, 0.25053367, 0.19894916,
971
+ 0.13792117, 0.09824532, 0.02916753],
972
+ [14.61464119, 2.45070267, 1.24153244, 0.74807048, 0.50118381, 0.36617002, 0.27464288, 0.22545385,
973
+ 0.17026083, 0.13792117, 0.09824532, 0.02916753],
974
+ [14.61464119, 2.45070267, 1.28281462, 0.803307, 0.54755926, 0.41087446, 0.32104823, 0.25053367, 0.19894916,
975
+ 0.17026083, 0.13792117, 0.09824532, 0.02916753],
976
+ [14.61464119, 2.45070267, 1.28281462, 0.803307, 0.57119018, 0.43325692, 0.34370604, 0.27464288, 0.22545385,
977
+ 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
978
+ [14.61464119, 2.45070267, 1.28281462, 0.83188516, 0.59516323, 0.45573691, 0.36617002, 0.29807833,
979
+ 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
980
+ [14.61464119, 2.45070267, 1.28281462, 0.83188516, 0.59516323, 0.45573691, 0.36617002, 0.32104823,
981
+ 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
982
+ [14.61464119, 2.84484982, 1.51179266, 0.95350921, 0.69515091, 0.52423614, 0.41087446, 0.34370604,
983
+ 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
984
+ 0.02916753],
985
+ [14.61464119, 2.84484982, 1.51179266, 0.95350921, 0.69515091, 0.52423614, 0.43325692, 0.36617002,
986
+ 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
987
+ 0.02916753],
988
+ [14.61464119, 2.84484982, 1.56271636, 0.98595673, 0.72133851, 0.54755926, 0.45573691, 0.38853383,
989
+ 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117,
990
+ 0.09824532, 0.02916753],
991
+ [14.61464119, 2.84484982, 1.56271636, 1.01931262, 0.74807048, 0.57119018, 0.4783645, 0.41087446, 0.36617002,
992
+ 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117,
993
+ 0.09824532, 0.02916753],
994
+ [14.61464119, 2.84484982, 1.56271636, 1.01931262, 0.74807048, 0.59516323, 0.50118381, 0.43325692,
995
+ 0.38853383, 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916,
996
+ 0.17026083, 0.13792117, 0.09824532, 0.02916753],
997
+ ],
998
+ 1.50: [
999
+ [14.61464119, 0.54755926, 0.02916753],
1000
+ [14.61464119, 0.803307, 0.25053367, 0.02916753],
1001
+ [14.61464119, 0.86115354, 0.32104823, 0.09824532, 0.02916753],
1002
+ [14.61464119, 1.24153244, 0.54755926, 0.25053367, 0.09824532, 0.02916753],
1003
+ [14.61464119, 1.56271636, 0.72133851, 0.36617002, 0.19894916, 0.09824532, 0.02916753],
1004
+ [14.61464119, 1.61558151, 0.803307, 0.45573691, 0.27464288, 0.17026083, 0.09824532, 0.02916753],
1005
+ [14.61464119, 1.61558151, 0.83188516, 0.52423614, 0.34370604, 0.25053367, 0.17026083, 0.09824532,
1006
+ 0.02916753],
1007
+ [14.61464119, 1.84880662, 0.95350921, 0.59516323, 0.38853383, 0.27464288, 0.19894916, 0.13792117,
1008
+ 0.09824532, 0.02916753],
1009
+ [14.61464119, 1.84880662, 0.95350921, 0.59516323, 0.41087446, 0.29807833, 0.22545385, 0.17026083,
1010
+ 0.13792117, 0.09824532, 0.02916753],
1011
+ [14.61464119, 1.84880662, 0.95350921, 0.61951244, 0.43325692, 0.32104823, 0.25053367, 0.19894916,
1012
+ 0.17026083, 0.13792117, 0.09824532, 0.02916753],
1013
+ [14.61464119, 2.19988537, 1.12534678, 0.72133851, 0.50118381, 0.36617002, 0.27464288, 0.22545385,
1014
+ 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
1015
+ [14.61464119, 2.19988537, 1.12534678, 0.72133851, 0.50118381, 0.36617002, 0.29807833, 0.25053367,
1016
+ 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
1017
+ [14.61464119, 2.36326075, 1.24153244, 0.803307, 0.57119018, 0.43325692, 0.34370604, 0.29807833, 0.25053367,
1018
+ 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
1019
+ [14.61464119, 2.36326075, 1.24153244, 0.803307, 0.57119018, 0.43325692, 0.34370604, 0.29807833, 0.27464288,
1020
+ 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
1021
+ [14.61464119, 2.36326075, 1.24153244, 0.803307, 0.59516323, 0.45573691, 0.36617002, 0.32104823, 0.29807833,
1022
+ 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
1023
+ [14.61464119, 2.36326075, 1.24153244, 0.803307, 0.59516323, 0.45573691, 0.38853383, 0.34370604, 0.32104823,
1024
+ 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532,
1025
+ 0.02916753],
1026
+ [14.61464119, 2.45070267, 1.32549286, 0.86115354, 0.64427125, 0.50118381, 0.41087446, 0.36617002,
1027
+ 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117,
1028
+ 0.09824532, 0.02916753],
1029
+ [14.61464119, 2.45070267, 1.36964464, 0.92192322, 0.69515091, 0.54755926, 0.45573691, 0.41087446,
1030
+ 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083,
1031
+ 0.13792117, 0.09824532, 0.02916753],
1032
+ [14.61464119, 2.45070267, 1.41535246, 0.95350921, 0.72133851, 0.57119018, 0.4783645, 0.43325692, 0.38853383,
1033
+ 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083,
1034
+ 0.13792117, 0.09824532, 0.02916753],
1035
+ ],
1036
+ }
1037
+
1038
+ def get_sigmas(self, coeff, steps, denoise):
1039
+ total_steps = steps
1040
+ if denoise < 1.0:
1041
+ if denoise <= 0.0:
1042
+ return (torch.FloatTensor([]),)
1043
+ total_steps = round(steps * denoise)
1044
+
1045
+ if steps <= 20:
1046
+ sigmas = self.NOISE_LEVELS[round(coeff, 2)][steps-2][:]
1047
+ else:
1048
+ sigmas = self.NOISE_LEVELS[round(coeff, 2)][-1][:]
1049
+ sigmas = loglinear_interp(sigmas, steps + 1)
1050
+
1051
+ sigmas = sigmas[-(total_steps + 1):]
1052
+ sigmas[-1] = 0
1053
+ return (torch.FloatTensor(sigmas), )
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/styleAlign.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from comfy.model_patcher import ModelPatcher
4
+ from typing import Union
5
+
6
+ T = torch.Tensor
7
+
8
+
9
+ def exists(val):
10
+ return val is not None
11
+
12
+
13
+ def default(val, d):
14
+ if exists(val):
15
+ return val
16
+ return d
17
+
18
+
19
+ class StyleAlignedArgs:
20
+ def __init__(self, share_attn: str) -> None:
21
+ self.adain_keys = "k" in share_attn
22
+ self.adain_values = "v" in share_attn
23
+ self.adain_queries = "q" in share_attn
24
+
25
+ share_attention: bool = True
26
+ adain_queries: bool = True
27
+ adain_keys: bool = True
28
+ adain_values: bool = True
29
+
30
+
31
+ def expand_first(
32
+ feat: T,
33
+ scale=1.0,
34
+ ) -> T:
35
+ """
36
+ Expand the first element so it has the same shape as the rest of the batch.
37
+ """
38
+ b = feat.shape[0]
39
+ feat_style = torch.stack((feat[0], feat[b // 2])).unsqueeze(1)
40
+ if scale == 1:
41
+ feat_style = feat_style.expand(2, b // 2, *feat.shape[1:])
42
+ else:
43
+ feat_style = feat_style.repeat(1, b // 2, 1, 1, 1)
44
+ feat_style = torch.cat([feat_style[:, :1], scale * feat_style[:, 1:]], dim=1)
45
+ return feat_style.reshape(*feat.shape)
46
+
47
+
48
+ def concat_first(feat: T, dim=2, scale=1.0) -> T:
49
+ """
50
+ concat the the feature and the style feature expanded above
51
+ """
52
+ feat_style = expand_first(feat, scale=scale)
53
+ return torch.cat((feat, feat_style), dim=dim)
54
+
55
+
56
+ def calc_mean_std(feat, eps: float = 1e-5) -> "tuple[T, T]":
57
+ feat_std = (feat.var(dim=-2, keepdims=True) + eps).sqrt()
58
+ feat_mean = feat.mean(dim=-2, keepdims=True)
59
+ return feat_mean, feat_std
60
+
61
+ def adain(feat: T) -> T:
62
+ feat_mean, feat_std = calc_mean_std(feat)
63
+ feat_style_mean = expand_first(feat_mean)
64
+ feat_style_std = expand_first(feat_std)
65
+ feat = (feat - feat_mean) / feat_std
66
+ feat = feat * feat_style_std + feat_style_mean
67
+ return feat
68
+
69
+ class SharedAttentionProcessor:
70
+ def __init__(self, args: StyleAlignedArgs, scale: float):
71
+ self.args = args
72
+ self.scale = scale
73
+
74
+ def __call__(self, q, k, v, extra_options):
75
+ if self.args.adain_queries:
76
+ q = adain(q)
77
+ if self.args.adain_keys:
78
+ k = adain(k)
79
+ if self.args.adain_values:
80
+ v = adain(v)
81
+ if self.args.share_attention:
82
+ k = concat_first(k, -2, scale=self.scale)
83
+ v = concat_first(v, -2)
84
+
85
+ return q, k, v
86
+
87
+
88
+ def get_norm_layers(
89
+ layer: nn.Module,
90
+ norm_layers_: "dict[str, list[Union[nn.GroupNorm, nn.LayerNorm]]]",
91
+ share_layer_norm: bool,
92
+ share_group_norm: bool,
93
+ ):
94
+ if isinstance(layer, nn.LayerNorm) and share_layer_norm:
95
+ norm_layers_["layer"].append(layer)
96
+ if isinstance(layer, nn.GroupNorm) and share_group_norm:
97
+ norm_layers_["group"].append(layer)
98
+ else:
99
+ for child_layer in layer.children():
100
+ get_norm_layers(
101
+ child_layer, norm_layers_, share_layer_norm, share_group_norm
102
+ )
103
+
104
+
105
+ def register_norm_forward(
106
+ norm_layer: Union[nn.GroupNorm, nn.LayerNorm],
107
+ ) -> Union[nn.GroupNorm, nn.LayerNorm]:
108
+ if not hasattr(norm_layer, "orig_forward"):
109
+ setattr(norm_layer, "orig_forward", norm_layer.forward)
110
+ orig_forward = norm_layer.orig_forward
111
+
112
+ def forward_(hidden_states: T) -> T:
113
+ n = hidden_states.shape[-2]
114
+ hidden_states = concat_first(hidden_states, dim=-2)
115
+ hidden_states = orig_forward(hidden_states) # type: ignore
116
+ return hidden_states[..., :n, :]
117
+
118
+ norm_layer.forward = forward_ # type: ignore
119
+ return norm_layer
120
+
121
+
122
+ def register_shared_norm(
123
+ model: ModelPatcher,
124
+ share_group_norm: bool = True,
125
+ share_layer_norm: bool = True,
126
+ ):
127
+ norm_layers = {"group": [], "layer": []}
128
+ get_norm_layers(model.model, norm_layers, share_layer_norm, share_group_norm)
129
+ print(
130
+ f"Patching {len(norm_layers['group'])} group norms, {len(norm_layers['layer'])} layer norms."
131
+ )
132
+ return [register_norm_forward(layer) for layer in norm_layers["group"]] + [
133
+ register_norm_forward(layer) for layer in norm_layers["layer"]
134
+ ]
135
+
136
+
137
+ SHARE_NORM_OPTIONS = ["both", "group", "layer", "disabled"]
138
+ SHARE_ATTN_OPTIONS = ["q+k", "q+k+v", "disabled"]
139
+
140
+
141
+ def styleAlignBatch(model, share_norm, share_attn, scale=1.0):
142
+ m = model.clone()
143
+ share_group_norm = share_norm in ["group", "both"]
144
+ share_layer_norm = share_norm in ["layer", "both"]
145
+ register_shared_norm(model, share_group_norm, share_layer_norm)
146
+ args = StyleAlignedArgs(share_attn)
147
+ m.set_model_attn1_patch(SharedAttentionProcessor(args, scale))
148
+ return m
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/translate.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #credit to shadowcz007 for this module
2
+ #from https://github.com/shadowcz007/comfyui-mixlab-nodes/blob/main/nodes/TextGenerateNode.py
3
+ import re
4
+ import os
5
+ import folder_paths
6
+
7
+ import comfy.utils
8
+ import torch
9
+ from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
10
+
11
+ from .utils import install_package
12
+ try:
13
+ from lark import Lark, Transformer, v_args
14
+ except:
15
+ print('install lark...')
16
+ install_package('lark')
17
+ from lark import Lark, Transformer, v_args
18
+
19
+ model_path = os.path.join(folder_paths.models_dir, 'prompt_generator')
20
+ zh_en_model_path = os.path.join(model_path, 'opus-mt-zh-en')
21
+ zh_en_model, zh_en_tokenizer = None, None
22
+
23
+ def correct_prompt_syntax(prompt=""):
24
+ # print("input prompt",prompt)
25
+ corrected_elements = []
26
+ # 处理成统一的英文标点
27
+ prompt = prompt.replace('(', '(').replace(')', ')').replace(',', ',').replace(';', ',').replace('。', '.').replace(':',':').replace('\\',',')
28
+ # 删除多余的空格
29
+ prompt = re.sub(r'\s+', ' ', prompt).strip()
30
+ prompt = prompt.replace("< ","<").replace(" >",">").replace("( ","(").replace(" )",")").replace("[ ","[").replace(' ]',']')
31
+
32
+ # 分词
33
+ prompt_elements = prompt.split(',')
34
+
35
+ def balance_brackets(element, open_bracket, close_bracket):
36
+ open_brackets_count = element.count(open_bracket)
37
+ close_brackets_count = element.count(close_bracket)
38
+ return element + close_bracket * (open_brackets_count - close_brackets_count)
39
+
40
+ for element in prompt_elements:
41
+ element = element.strip()
42
+
43
+ # 处理空元素
44
+ if not element:
45
+ continue
46
+
47
+ # 检查并处理圆括号、方括号、尖括号
48
+ if element[0] in '([':
49
+ corrected_element = balance_brackets(element, '(', ')') if element[0] == '(' else balance_brackets(element, '[', ']')
50
+ elif element[0] == '<':
51
+ corrected_element = balance_brackets(element, '<', '>')
52
+ else:
53
+ # 删除开头的右括号或右方括号
54
+ corrected_element = element.lstrip(')]')
55
+
56
+ corrected_elements.append(corrected_element)
57
+
58
+ # 重组修正后的prompt
59
+ return ','.join(corrected_elements)
60
+
61
+ def detect_language(input_str):
62
+ # 统计中文和英文字符的数量
63
+ count_cn = count_en = 0
64
+ for char in input_str:
65
+ if '\u4e00' <= char <= '\u9fff':
66
+ count_cn += 1
67
+ elif char.isalpha():
68
+ count_en += 1
69
+
70
+ # 根据统计的字符数量判断主要语言
71
+ if count_cn > count_en:
72
+ return "cn"
73
+ elif count_en > count_cn:
74
+ return "en"
75
+ else:
76
+ return "unknow"
77
+
78
+ def has_chinese(text):
79
+ has_cn = False
80
+ _text = text
81
+ _text = re.sub(r'<.*?>', '', _text)
82
+ _text = re.sub(r'__.*?__', '', _text)
83
+ _text = re.sub(r'embedding:.*?$', '', _text)
84
+ for char in _text:
85
+ if '\u4e00' <= char <= '\u9fff':
86
+ has_cn = True
87
+ break
88
+ elif char.isalpha():
89
+ continue
90
+ return has_cn
91
+
92
+ def translate(text):
93
+ global zh_en_model_path, zh_en_model, zh_en_tokenizer
94
+
95
+ if not os.path.exists(zh_en_model_path):
96
+ zh_en_model_path = 'Helsinki-NLP/opus-mt-zh-en'
97
+
98
+ if zh_en_model is None:
99
+
100
+ zh_en_model = AutoModelForSeq2SeqLM.from_pretrained(zh_en_model_path).eval()
101
+ zh_en_tokenizer = AutoTokenizer.from_pretrained(zh_en_model_path, padding=True, truncation=True)
102
+
103
+ zh_en_model.to("cuda" if torch.cuda.is_available() else "cpu")
104
+ with torch.no_grad():
105
+ encoded = zh_en_tokenizer([text], return_tensors="pt")
106
+ encoded.to(zh_en_model.device)
107
+ sequences = zh_en_model.generate(**encoded)
108
+ return zh_en_tokenizer.batch_decode(sequences, skip_special_tokens=True)[0]
109
+
110
+ @v_args(inline=True) # Decorator to flatten the tree directly into the function arguments
111
+ class ChinesePromptTranslate(Transformer):
112
+
113
+ def sentence(self, *args):
114
+ return ", ".join(args)
115
+
116
+ def phrase(self, *args):
117
+ return "".join(args)
118
+
119
+ def emphasis(self, *args):
120
+ # Reconstruct the emphasis with translated content
121
+ return "(" + "".join(args) + ")"
122
+
123
+ def weak_emphasis(self, *args):
124
+ print('weak_emphasis:', args)
125
+ return "[" + "".join(args) + "]"
126
+
127
+ def embedding(self, *args):
128
+ print('prompt embedding', args[0])
129
+ if len(args) == 1:
130
+ embedding_name = str(args[0])
131
+ return f"embedding:{embedding_name}"
132
+ elif len(args) > 1:
133
+ embedding_name, *numbers = args
134
+
135
+ if len(numbers) == 2:
136
+ return f"embedding:{embedding_name}:{numbers[0]}:{numbers[1]}"
137
+ elif len(numbers) == 1:
138
+ return f"embedding:{embedding_name}:{numbers[0]}"
139
+ else:
140
+ return f"embedding:{embedding_name}"
141
+
142
+ def lora(self, *args):
143
+ if len(args) == 1:
144
+ return f"<lora:{args[0]}>"
145
+ elif len(args) > 1:
146
+ # print('lora', args)
147
+ _, loar_name, *numbers = args
148
+ loar_name = str(loar_name).strip()
149
+ if len(numbers) == 2:
150
+ return f"<lora:{loar_name}:{numbers[0]}:{numbers[1]}>"
151
+ elif len(numbers) == 1:
152
+ return f"<lora:{loar_name}:{numbers[0]}>"
153
+ else:
154
+ return f"<lora:{loar_name}>"
155
+
156
+ def weight(self, word, number):
157
+ translated_word = translate(str(word)).rstrip('.')
158
+ return f"({translated_word}:{str(number).strip()})"
159
+
160
+ def schedule(self, *args):
161
+ print('prompt schedule', args)
162
+ data = [str(arg).strip() for arg in args]
163
+
164
+ return f"[{':'.join(data)}]"
165
+
166
+ def word(self, word):
167
+ # Translate each word using the dictionary
168
+ word = str(word)
169
+ match_cn = re.search(r'@.*?@', word)
170
+ if re.search(r'__.*?__', word):
171
+ return word.rstrip('.')
172
+ elif match_cn:
173
+ chinese = match_cn.group()
174
+ before = word.split('@', 1)
175
+ before = before[0] if len(before) > 0 else ''
176
+ before = translate(str(before)).rstrip('.') if before else ''
177
+ after = word.rsplit('@', 1)
178
+ after = after[len(after)-1] if len(after) > 1 else ''
179
+ after = translate(after).rstrip('.') if after else ''
180
+ return before + chinese.replace('@', '').rstrip('.') + after
181
+ elif detect_language(word) == "cn":
182
+ return translate(word).rstrip('.')
183
+ else:
184
+ return word.rstrip('.')
185
+
186
+
187
+ #定义Prompt文法
188
+ grammar = r"""
189
+ start: sentence
190
+ sentence: phrase ("," phrase)*
191
+ phrase: emphasis | weight | word | lora | embedding | schedule
192
+ emphasis: "(" sentence ")" -> emphasis
193
+ | "[" sentence "]" -> weak_emphasis
194
+ weight: "(" word ":" NUMBER ")"
195
+ schedule: "[" word ":" word ":" NUMBER "]"
196
+ lora: "<" WORD ":" WORD (":" NUMBER)? (":" NUMBER)? ">"
197
+ embedding: "embedding" ":" WORD (":" NUMBER)? (":" NUMBER)?
198
+ word: WORD
199
+
200
+ NUMBER: /\s*-?\d+(\.\d+)?\s*/
201
+ WORD: /[^,:\(\)\[\]<>]+/
202
+ """
203
+ def zh_to_en(text):
204
+ global zh_en_model_path, zh_en_model, zh_en_tokenizer
205
+ # 进度条
206
+ pbar = comfy.utils.ProgressBar(len(text) + 1)
207
+ texts = [correct_prompt_syntax(t) for t in text]
208
+
209
+ install_package('sentencepiece', '0.2.0')
210
+
211
+ if not os.path.exists(zh_en_model_path):
212
+ zh_en_model_path = 'Helsinki-NLP/opus-mt-zh-en'
213
+
214
+ if zh_en_model is None:
215
+ zh_en_model = AutoModelForSeq2SeqLM.from_pretrained(zh_en_model_path).eval()
216
+ zh_en_tokenizer = AutoTokenizer.from_pretrained(zh_en_model_path, padding=True, truncation=True)
217
+
218
+ zh_en_model.to("cuda" if torch.cuda.is_available() else "cpu")
219
+
220
+ prompt_result = []
221
+
222
+ en_texts = []
223
+
224
+ for t in texts:
225
+ if t:
226
+ # translated_text = translated_word = translate(zh_en_tokenizer,zh_en_model,str(t))
227
+ parser = Lark(grammar, start="start", parser="lalr", transformer=ChinesePromptTranslate())
228
+ # print('t',t)
229
+ result = parser.parse(t).children
230
+ # print('en_result',result)
231
+ # en_text=translate(zh_en_tokenizer,zh_en_model,text_without_syntax)
232
+ en_texts.append(result[0])
233
+
234
+ zh_en_model.to('cpu')
235
+ # print("test en_text", en_texts)
236
+ # en_text.to("cuda" if torch.cuda.is_available() else "cpu")
237
+
238
+ pbar.update(1)
239
+ for t in en_texts:
240
+ prompt_result.append(t)
241
+ pbar.update(1)
242
+
243
+ # print('prompt_result', prompt_result, )
244
+ if len(prompt_result) == 0:
245
+ prompt_result = [""]
246
+
247
+ return prompt_result
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/utils.py ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class AlwaysEqualProxy(str):
2
+ def __eq__(self, _):
3
+ return True
4
+
5
+ def __ne__(self, _):
6
+ return False
7
+
8
+ class TautologyStr(str):
9
+ def __ne__(self, other):
10
+ return False
11
+
12
+ class ByPassTypeTuple(tuple):
13
+ def __getitem__(self, index):
14
+ if index>0:
15
+ index=0
16
+ item = super().__getitem__(index)
17
+ if isinstance(item, str):
18
+ return TautologyStr(item)
19
+ return item
20
+
21
+ comfy_ui_revision = None
22
+ def get_comfyui_revision():
23
+ try:
24
+ import git
25
+ import os
26
+ import folder_paths
27
+ repo = git.Repo(os.path.dirname(folder_paths.__file__))
28
+ comfy_ui_revision = len(list(repo.iter_commits('HEAD')))
29
+ except:
30
+ comfy_ui_revision = "Unknown"
31
+ return comfy_ui_revision
32
+
33
+
34
+ import sys
35
+ import importlib.util
36
+ import importlib.metadata
37
+ import comfy.model_management as mm
38
+ import logging
39
+ import gc
40
+ from packaging import version
41
+ from server import PromptServer
42
+
43
+ LOG = logging.getLogger(__name__)
44
+
45
+ def is_package_installed(package):
46
+ try:
47
+ module = importlib.util.find_spec(package)
48
+ return module is not None
49
+ except ImportError as e:
50
+ print(e)
51
+ return False
52
+
53
+ def install_package(package, v=None, compare=True, compare_version=None):
54
+ run_install = True
55
+ if is_package_installed(package):
56
+ try:
57
+ installed_version = importlib.metadata.version(package)
58
+ if v is not None:
59
+ if compare_version is None:
60
+ compare_version = v
61
+ if not compare or version.parse(installed_version) >= version.parse(compare_version):
62
+ run_install = False
63
+ else:
64
+ run_install = False
65
+ except:
66
+ run_install = False
67
+
68
+ if run_install:
69
+ import subprocess
70
+ package_command = package + '==' + v if v is not None else package
71
+ PromptServer.instance.send_sync("easyuse-toast", {'content': f"Installing {package_command}...", 'duration': 5000})
72
+ result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', package_command], capture_output=True, text=True)
73
+ if result.returncode == 0:
74
+ PromptServer.instance.send_sync("easyuse-toast", {'content': f"{package} installed successfully", 'type': 'success', 'duration': 5000})
75
+ print(f"Package {package} installed successfully")
76
+ return True
77
+ else:
78
+ PromptServer.instance.send_sync("easyuse-toast", {'content': f"{package} installed failed", 'type': 'error', 'duration': 5000})
79
+ print(f"Package {package} installed failed")
80
+ return False
81
+ else:
82
+ return False
83
+
84
+ def compare_revision(num):
85
+ global comfy_ui_revision
86
+ if not comfy_ui_revision:
87
+ comfy_ui_revision = get_comfyui_revision()
88
+ return True if comfy_ui_revision == 'Unknown' or int(comfy_ui_revision) >= num else False
89
+
90
+ def find_tags(string: str, sep="/") -> list[str]:
91
+ """
92
+ find tags from string use the sep for split
93
+ Note: string may contain the \\ or / for path separator
94
+ """
95
+ if not string:
96
+ return []
97
+ string = string.replace("\\", "/")
98
+ while "//" in string:
99
+ string = string.replace("//", "/")
100
+ if string and sep in string:
101
+ return string.split(sep)[:-1]
102
+ return []
103
+
104
+
105
+ from comfy.model_base import BaseModel
106
+ import comfy.supported_models
107
+ import comfy.supported_models_base
108
+ def get_sd_version(model):
109
+ base: BaseModel = model.model
110
+ model_config: comfy.supported_models.supported_models_base.BASE = base.model_config
111
+ if isinstance(model_config, comfy.supported_models.SDXL):
112
+ return 'sdxl'
113
+ elif isinstance(model_config, comfy.supported_models.SDXLRefiner):
114
+ return 'sdxl_refiner'
115
+ elif isinstance(
116
+ model_config, (comfy.supported_models.SD15, comfy.supported_models.SD20)
117
+ ):
118
+ return 'sd1'
119
+ elif isinstance(
120
+ model_config, (comfy.supported_models.SVD_img2vid)
121
+ ):
122
+ return 'svd'
123
+ elif isinstance(model_config, comfy.supported_models.SD3):
124
+ return 'sd3'
125
+ elif isinstance(model_config, comfy.supported_models.HunyuanDiT):
126
+ return 'hydit'
127
+ elif isinstance(model_config, comfy.supported_models.Flux):
128
+ return 'flux'
129
+ elif isinstance(model_config, comfy.supported_models.GenmoMochi):
130
+ return 'mochi'
131
+ else:
132
+ return 'unknown'
133
+
134
+ def find_nearest_steps(clip_id, prompt):
135
+ """Find the nearest KSampler or preSampling node that references the given id."""
136
+ def check_link_to_clip(node_id, clip_id, visited=None, node=None):
137
+ """Check if a given node links directly or indirectly to a loader node."""
138
+ if visited is None:
139
+ visited = set()
140
+
141
+ if node_id in visited:
142
+ return False
143
+ visited.add(node_id)
144
+ if "pipe" in node["inputs"]:
145
+ link_ids = node["inputs"]["pipe"]
146
+ for id in link_ids:
147
+ if id != 0 and id == str(clip_id):
148
+ return True
149
+ return False
150
+
151
+ for id in prompt:
152
+ node = prompt[id]
153
+ if "Sampler" in node["class_type"] or "sampler" in node["class_type"] or "Sampling" in node["class_type"]:
154
+ # Check if this KSampler node directly or indirectly references the given CLIPTextEncode node
155
+ if check_link_to_clip(id, clip_id, None, node):
156
+ steps = node["inputs"]["steps"] if "steps" in node["inputs"] else 1
157
+ return steps
158
+ return 1
159
+
160
+ def find_wildcards_seed(clip_id, text, prompt):
161
+ """ Find easy wildcards seed value"""
162
+ def find_link_clip_id(id, seed, wildcard_id):
163
+ node = prompt[id]
164
+ if "positive" in node['inputs']:
165
+ link_ids = node["inputs"]["positive"]
166
+ if type(link_ids) == list:
167
+ for id in link_ids:
168
+ if id != 0:
169
+ if id == wildcard_id:
170
+ wildcard_node = prompt[wildcard_id]
171
+ seed = wildcard_node["inputs"]["seed"] if "seed" in wildcard_node["inputs"] else None
172
+ if seed is None:
173
+ seed = wildcard_node["inputs"]["seed_num"] if "seed_num" in wildcard_node["inputs"] else None
174
+ return seed
175
+ else:
176
+ return find_link_clip_id(id, seed, wildcard_id)
177
+ else:
178
+ return None
179
+ else:
180
+ return None
181
+ if "__" in text:
182
+ seed = None
183
+ for id in prompt:
184
+ node = prompt[id]
185
+ if "wildcards" in node["class_type"]:
186
+ wildcard_id = id
187
+ return find_link_clip_id(str(clip_id), seed, wildcard_id)
188
+ return seed
189
+ else:
190
+ return None
191
+
192
+ def is_linked_styles_selector(prompt, unique_id, prompt_type='positive'):
193
+ unique_id = unique_id.split('.')[len(unique_id.split('.')) - 1] if "." in unique_id else unique_id
194
+ inputs_values = prompt[unique_id]['inputs'][prompt_type] if prompt_type in prompt[unique_id][
195
+ 'inputs'] else None
196
+ if type(inputs_values) == list and inputs_values != 'undefined' and inputs_values[0]:
197
+ return True if prompt[inputs_values[0]] and prompt[inputs_values[0]]['class_type'] == 'easy stylesSelector' else False
198
+ else:
199
+ return False
200
+
201
+ use_mirror = False
202
+ def get_local_filepath(url, dirname, local_file_name=None):
203
+ """Get local file path when is already downloaded or download it"""
204
+ import os
205
+ from server import PromptServer
206
+ from urllib.parse import urlparse
207
+ from torch.hub import download_url_to_file
208
+ global use_mirror
209
+ if not os.path.exists(dirname):
210
+ os.makedirs(dirname)
211
+ if not local_file_name:
212
+ parsed_url = urlparse(url)
213
+ local_file_name = os.path.basename(parsed_url.path)
214
+ destination = os.path.join(dirname, local_file_name)
215
+ if not os.path.exists(destination):
216
+ try:
217
+ if use_mirror:
218
+ url = url.replace('huggingface.co', 'hf-mirror.com')
219
+ print(f'downloading {url} to {destination}')
220
+ PromptServer.instance.send_sync("easyuse-toast", {'content': f'Downloading model to {destination}, please wait...', 'duration': 10000})
221
+ download_url_to_file(url, destination)
222
+ except Exception as e:
223
+ use_mirror = True
224
+ url = url.replace('huggingface.co', 'hf-mirror.com')
225
+ print(f'Unable to download from huggingface, trying mirror: {url}')
226
+ PromptServer.instance.send_sync("easyuse-toast", {'content': f'Unable to connect to huggingface, trying mirror: {url}', 'duration': 10000})
227
+ try:
228
+ download_url_to_file(url, destination)
229
+ except Exception as err:
230
+ error_msg = str(err.args[0]) if err.args else str(err)
231
+ PromptServer.instance.send_sync("easyuse-toast",
232
+ {'content': f'Unable to download model from {url}', 'type':'error'})
233
+ raise Exception(f'Download failed. Original URL and mirror both failed.\nError: {error_msg}')
234
+ return destination
235
+
236
+ def to_lora_patch_dict(state_dict: dict) -> dict:
237
+ """ Convert raw lora state_dict to patch_dict that can be applied on
238
+ modelpatcher."""
239
+ patch_dict = {}
240
+ for k, w in state_dict.items():
241
+ model_key, patch_type, weight_index = k.split('::')
242
+ if model_key not in patch_dict:
243
+ patch_dict[model_key] = {}
244
+ if patch_type not in patch_dict[model_key]:
245
+ patch_dict[model_key][patch_type] = [None] * 16
246
+ patch_dict[model_key][patch_type][int(weight_index)] = w
247
+
248
+ patch_flat = {}
249
+ for model_key, v in patch_dict.items():
250
+ for patch_type, weight_list in v.items():
251
+ patch_flat[model_key] = (patch_type, weight_list)
252
+
253
+ return patch_flat
254
+
255
+ def easySave(images, filename_prefix, output_type, prompt=None, extra_pnginfo=None):
256
+ """Save or Preview Image"""
257
+ from nodes import PreviewImage, SaveImage
258
+ if output_type in ["Hide", "None"]:
259
+ return list()
260
+ elif output_type in ["Preview", "Preview&Choose"]:
261
+ filename_prefix = 'easyPreview'
262
+ results = PreviewImage().save_images(images, filename_prefix, prompt, extra_pnginfo)
263
+ return results['ui']['images']
264
+ else:
265
+ results = SaveImage().save_images(images, filename_prefix, prompt, extra_pnginfo)
266
+ return results['ui']['images']
267
+
268
+ def getMetadata(filepath):
269
+ with open(filepath, "rb") as file:
270
+ # https://github.com/huggingface/safetensors#format
271
+ # 8 bytes: N, an unsigned little-endian 64-bit integer, containing the size of the header
272
+ header_size = int.from_bytes(file.read(8), "little", signed=False)
273
+
274
+ if header_size <= 0:
275
+ raise BufferError("Invalid header size")
276
+
277
+ header = file.read(header_size)
278
+ if header_size <= 0:
279
+ raise BufferError("Invalid header")
280
+
281
+ return header
282
+
283
+ def cleanGPUUsedForce():
284
+ from .cache import remove_cache
285
+
286
+ remove_cache("*")
287
+ gc.collect()
288
+ try:
289
+ import torch
290
+ except (ImportError, OSError, RuntimeError) as exc:
291
+ LOG.debug("Skipping CUDA synchronize during cleanGPUUsedForce: torch import failed: %s", exc)
292
+ else:
293
+ try:
294
+ if torch.cuda.is_available():
295
+ torch.cuda.synchronize()
296
+ except (AttributeError, OSError, RuntimeError) as exc:
297
+ LOG.debug("Skipping CUDA synchronize during cleanGPUUsedForce: %s", exc)
298
+
299
+ mm.unload_all_models()
300
+ mm.soft_empty_cache()
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/wildcards.py ADDED
@@ -0,0 +1,476 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import random
4
+ import re
5
+ from math import prod
6
+
7
+ import yaml
8
+
9
+ import folder_paths
10
+
11
+ from .log import log_node_info
12
+
13
+ easy_wildcard_dict = {}
14
+
15
+ def get_wildcard_list():
16
+ return [f"__{x}__" for x in easy_wildcard_dict.keys()]
17
+
18
+ def wildcard_normalize(x):
19
+ return x.replace("\\", "/").lower()
20
+
21
+ def read_wildcard(k, v):
22
+ if isinstance(v, list):
23
+ k = wildcard_normalize(k)
24
+ easy_wildcard_dict[k] = v
25
+ elif isinstance(v, dict):
26
+ for k2, v2 in v.items():
27
+ new_key = f"{k}/{k2}"
28
+ new_key = wildcard_normalize(new_key)
29
+ read_wildcard(new_key, v2)
30
+
31
+ def read_wildcard_dict(wildcard_path):
32
+ global easy_wildcard_dict
33
+ for root, directories, files in os.walk(wildcard_path, followlinks=True):
34
+ for file in files:
35
+ if file.endswith('.txt'):
36
+ file_path = os.path.join(root, file)
37
+ rel_path = os.path.relpath(file_path, wildcard_path)
38
+ key = os.path.splitext(rel_path)[0].replace('\\', '/').lower()
39
+
40
+ try:
41
+ with open(file_path, 'r', encoding="UTF-8", errors="ignore") as f:
42
+ lines = f.read().splitlines()
43
+ easy_wildcard_dict[key] = lines
44
+ except UnicodeDecodeError:
45
+ with open(file_path, 'r', encoding="ISO-8859-1") as f:
46
+ lines = f.read().splitlines()
47
+ easy_wildcard_dict[key] = lines
48
+ elif file.endswith('.yaml'):
49
+ file_path = os.path.join(root, file)
50
+ with open(file_path, 'r', encoding="utf-8") as f:
51
+ yaml_data = yaml.load(f, Loader=yaml.FullLoader)
52
+
53
+ for k, v in yaml_data.items():
54
+ read_wildcard(k, v)
55
+ elif file.endswith('.json'):
56
+ file_path = os.path.join(root, file)
57
+ try:
58
+ with open(file_path, 'r', encoding="utf-8") as f:
59
+ json_data = json.load(f)
60
+ for key, value in json_data.items():
61
+ key = wildcard_normalize(key)
62
+ easy_wildcard_dict[key] = value
63
+ except ValueError:
64
+ print('json files load error')
65
+ return easy_wildcard_dict
66
+
67
+
68
+ def process(text, seed=None):
69
+
70
+ if seed is not None:
71
+ random.seed(seed)
72
+
73
+ def replace_options(string):
74
+ replacements_found = False
75
+
76
+ def replace_option(match):
77
+ nonlocal replacements_found
78
+ options = match.group(1).split('|')
79
+
80
+ multi_select_pattern = options[0].split('$$')
81
+ select_range = None
82
+ select_sep = ' '
83
+ range_pattern = r'(\d+)(-(\d+))?'
84
+ range_pattern2 = r'-(\d+)'
85
+
86
+ if len(multi_select_pattern) > 1:
87
+ r = re.match(range_pattern, options[0])
88
+
89
+ if r is None:
90
+ r = re.match(range_pattern2, options[0])
91
+ a = '1'
92
+ b = r.group(1).strip()
93
+ else:
94
+ a = r.group(1).strip()
95
+ b = r.group(3).strip()
96
+
97
+ if r is not None:
98
+ if b is not None and is_numeric_string(a) and is_numeric_string(b):
99
+ # PATTERN: num1-num2
100
+ select_range = int(a), int(b)
101
+ elif is_numeric_string(a):
102
+ # PATTERN: num
103
+ x = int(a)
104
+ select_range = (x, x)
105
+
106
+ if select_range is not None and len(multi_select_pattern) == 2:
107
+ # PATTERN: count$$
108
+ options[0] = multi_select_pattern[1]
109
+ elif select_range is not None and len(multi_select_pattern) == 3:
110
+ # PATTERN: count$$ sep $$
111
+ select_sep = multi_select_pattern[1]
112
+ options[0] = multi_select_pattern[2]
113
+
114
+ adjusted_probabilities = []
115
+
116
+ total_prob = 0
117
+
118
+ for option in options:
119
+ parts = option.split('::', 1)
120
+ if len(parts) == 2 and is_numeric_string(parts[0].strip()):
121
+ config_value = float(parts[0].strip())
122
+ else:
123
+ config_value = 1 # Default value if no configuration is provided
124
+
125
+ adjusted_probabilities.append(config_value)
126
+ total_prob += config_value
127
+
128
+ normalized_probabilities = [prob / total_prob for prob in adjusted_probabilities]
129
+
130
+ if select_range is None:
131
+ select_count = 1
132
+ else:
133
+ select_count = random.randint(select_range[0], select_range[1])
134
+
135
+ if select_count > len(options):
136
+ selected_items = options
137
+ else:
138
+ selected_items = random.choices(options, weights=normalized_probabilities, k=select_count)
139
+ selected_items = set(selected_items)
140
+
141
+ try_count = 0
142
+ while len(selected_items) < select_count and try_count < 10:
143
+ remaining_count = select_count - len(selected_items)
144
+ additional_items = random.choices(options, weights=normalized_probabilities, k=remaining_count)
145
+ selected_items |= set(additional_items)
146
+ try_count += 1
147
+
148
+ selected_items2 = [re.sub(r'^\s*[0-9.]+::', '', x, 1) for x in selected_items]
149
+ replacement = select_sep.join(selected_items2)
150
+ if '::' in replacement:
151
+ pass
152
+
153
+ replacements_found = True
154
+ return replacement
155
+
156
+ pattern = r'{([^{}]*?)}'
157
+ replaced_string = re.sub(pattern, replace_option, string)
158
+
159
+ return replaced_string, replacements_found
160
+
161
+ def replace_wildcard(string):
162
+ global easy_wildcard_dict
163
+ pattern = r"__([\w\s.\-+/*\\]+?)__"
164
+ matches = re.findall(pattern, string)
165
+ replacements_found = False
166
+
167
+ for match in matches:
168
+ keyword = match.lower()
169
+ keyword = wildcard_normalize(keyword)
170
+ if keyword in easy_wildcard_dict:
171
+ replacement = random.choice(easy_wildcard_dict[keyword])
172
+ replacements_found = True
173
+ string = string.replace(f"__{match}__", replacement, 1)
174
+ elif '*' in keyword:
175
+ subpattern = keyword.replace('*', '.*').replace('+', r'\+')
176
+ total_patterns = []
177
+ found = False
178
+ for k, v in easy_wildcard_dict.items():
179
+ if re.match(subpattern, k) is not None:
180
+ total_patterns += v
181
+ found = True
182
+
183
+ if found:
184
+ replacement = random.choice(total_patterns)
185
+ replacements_found = True
186
+ string = string.replace(f"__{match}__", replacement, 1)
187
+ elif '/' not in keyword:
188
+ string_fallback = string.replace(f"__{match}__", f"__*/{match}__", 1)
189
+ string, replacements_found = replace_wildcard(string_fallback)
190
+
191
+ return string, replacements_found
192
+
193
+ replace_depth = 100
194
+ stop_unwrap = False
195
+ while not stop_unwrap and replace_depth > 1:
196
+ replace_depth -= 1 # prevent infinite loop
197
+
198
+ # pass1: replace options
199
+ pass1, is_replaced1 = replace_options(text)
200
+
201
+ while is_replaced1:
202
+ pass1, is_replaced1 = replace_options(pass1)
203
+
204
+ # pass2: replace wildcards
205
+ text, is_replaced2 = replace_wildcard(pass1)
206
+ stop_unwrap = not is_replaced1 and not is_replaced2
207
+
208
+ return text
209
+
210
+
211
+ def is_numeric_string(input_str):
212
+ return re.match(r'^-?\d+(\.\d+)?$', input_str) is not None
213
+
214
+
215
+ def safe_float(x):
216
+ if is_numeric_string(x):
217
+ return float(x)
218
+ else:
219
+ return 1.0
220
+
221
+
222
+ def extract_lora_values(string):
223
+ pattern = r'<lora:([^>]+)>'
224
+ matches = re.findall(pattern, string)
225
+
226
+ def touch_lbw(text):
227
+ return re.sub(r'LBW=[A-Za-z][A-Za-z0-9_-]*:', r'LBW=', text)
228
+
229
+ items = [touch_lbw(match.strip(':')) for match in matches]
230
+
231
+ added = set()
232
+ result = []
233
+ for item in items:
234
+ item = item.split(':')
235
+
236
+ lora = None
237
+ a = None
238
+ b = None
239
+ lbw = None
240
+ lbw_a = None
241
+ lbw_b = None
242
+
243
+ if len(item) > 0:
244
+ lora = item[0]
245
+
246
+ for sub_item in item[1:]:
247
+ if is_numeric_string(sub_item):
248
+ if a is None:
249
+ a = float(sub_item)
250
+ elif b is None:
251
+ b = float(sub_item)
252
+ elif sub_item.startswith("LBW="):
253
+ for lbw_item in sub_item[4:].split(';'):
254
+ if lbw_item.startswith("A="):
255
+ lbw_a = safe_float(lbw_item[2:].strip())
256
+ elif lbw_item.startswith("B="):
257
+ lbw_b = safe_float(lbw_item[2:].strip())
258
+ elif lbw_item.strip() != '':
259
+ lbw = lbw_item
260
+
261
+ if a is None:
262
+ a = 1.0
263
+ if b is None:
264
+ b = 1.0
265
+
266
+ if lora is not None and lora not in added:
267
+ result.append((lora, a, b, lbw, lbw_a, lbw_b))
268
+ added.add(lora)
269
+
270
+ return result
271
+
272
+
273
+ def remove_lora_tags(string):
274
+ pattern = r'<lora:[^>]+>'
275
+ result = re.sub(pattern, '', string)
276
+
277
+ return result
278
+
279
+ def process_with_loras(wildcard_opt, model, clip, title="Positive", seed=None, can_load_lora=True, pipe_lora_stack=[], easyCache=None):
280
+ pass1 = process(wildcard_opt, seed)
281
+ loras = extract_lora_values(pass1)
282
+ pass2 = remove_lora_tags(pass1)
283
+
284
+ has_noodle_key = True if "__" in wildcard_opt else False
285
+ has_loras = True if loras != [] else False
286
+ show_wildcard_prompt = True if has_noodle_key or has_loras else False
287
+
288
+ if can_load_lora and has_loras:
289
+ for lora_name, model_weight, clip_weight, lbw, lbw_a, lbw_b in loras:
290
+ if (lora_name.split('.')[-1]) not in folder_paths.supported_pt_extensions:
291
+ lora_name = lora_name+".safetensors"
292
+ lora = {
293
+ "lora_name": lora_name, "model": model, "clip": clip, "model_strength": model_weight,
294
+ "clip_strength": clip_weight,
295
+ "lbw_a": lbw_a,
296
+ "lbw_b": lbw_b,
297
+ "lbw": lbw
298
+ }
299
+ model, clip = easyCache.load_lora(lora)
300
+ lora["model"] = model
301
+ lora["clip"] = clip
302
+ pipe_lora_stack.append(lora)
303
+
304
+ log_node_info("easy wildcards",f"{title}: {pass2}")
305
+ if pass1 != pass2:
306
+ log_node_info("easy wildcards",f'{title}_decode: {pass1}')
307
+
308
+ return model, clip, pass2, pass1, show_wildcard_prompt, pipe_lora_stack
309
+
310
+
311
+ def expand_wildcard(keyword: str) -> tuple[str]:
312
+ """传入文件通配符的关键词,从 easy_wildcard_dict 中获取通配符的所有选项。"""
313
+ global easy_wildcard_dict
314
+ if keyword in easy_wildcard_dict:
315
+ return tuple(easy_wildcard_dict[keyword])
316
+ elif '*' in keyword:
317
+ subpattern = keyword.replace('*', '.*').replace('+', r"\+")
318
+ total_pattern = []
319
+ for k, v in easy_wildcard_dict.items():
320
+ if re.match(subpattern, k) is not None:
321
+ total_pattern.extend(v)
322
+ if total_pattern:
323
+ return tuple(total_pattern)
324
+ elif '/' not in keyword:
325
+ return expand_wildcard(f"*/{keyword}")
326
+
327
+ def expand_options(options: str) -> tuple[str]:
328
+ """传入去掉 {} 的选项。
329
+ 展开选项通配符,返回该选项中的每一项,这里的每一项都是一个替换项。
330
+ 不会对选项内容进行任何处理,即便存在空格或特殊符号,也会原样返回。"""
331
+ return tuple(options.split("|"))
332
+
333
+
334
+ def decimal_to_irregular(n, bases):
335
+ """
336
+ 将十进制数转换为不规则进制
337
+
338
+ :param n: 十进制数
339
+ :param bases: 各位置的基数列表,从低位到高位
340
+ :return: 不规则进制表示的列表,从低位到高位
341
+ """
342
+ if n == 0:
343
+ return [0] * len(bases) if bases else [0]
344
+
345
+ digits = []
346
+ remaining = n
347
+
348
+ # 从低位到高位处理
349
+ for base in bases:
350
+ digit = remaining % base
351
+ digits.append(digit)
352
+ remaining = remaining // base
353
+
354
+ return digits
355
+
356
+
357
+ class WildcardProcessor:
358
+ """通配符处理器
359
+
360
+ 通配符格式:
361
+ + option : {a|b}
362
+ + wildcard: __keyword__ 通配符内容将从 Easy-Use 插件提供的 easy_wildcard_dict 中获取
363
+ """
364
+
365
+ RE_OPTIONS = re.compile(r"{([^{}]*?)}")
366
+ RE_WILDCARD = re.compile(r"__([\w\s.\-+/*\\]+?)__")
367
+ RE_REPLACER = re.compile(r"{([^{}]*?)}|__([\w\s.\-+/*\\]+?)__")
368
+
369
+ # 将输入的提示词转化成符合 python str.format 要求格式的模板,并将 option 和 wildcard 按照顺序在模板中留下 {0}, {1} 等占位符
370
+ template: str
371
+ # option、wildcard 的替换项列表,按照在模板中出现的顺序排列,相同的替换项列表只保留第一份
372
+ replacers: dict[int, tuple[str]]
373
+ # 占位符的编号和替换项列表的索引的映射,占位符编号按照在模板中出现的顺序排列,方便减少替换项的存储占用
374
+ placeholder_mapping: dict[str, int] # placeholder_id => replacer_id
375
+ # 各替换项列表的项数,按照在模板中出现的顺序排列,提前计算,方便后续使用
376
+ placeholder_choices: dict[str, int] # placeholder_id => len(replacer)
377
+
378
+ def __init__(self, text: str):
379
+ self.__make_template(text)
380
+ self.__total = None
381
+
382
+ def random(self, seed=None) -> str:
383
+ "从所有可能性中随机获取一个"
384
+ if seed is not None:
385
+ random.seed(seed)
386
+ return self.getn(random.randint(0, self.total() - 1))
387
+
388
+ def getn(self, n: int) -> str:
389
+ "从所有可能性中获取第 n 个,以 self.total() 为周期循环"
390
+ n = n % self.total()
391
+ indice = decimal_to_irregular(n, self.placeholder_choices.values())
392
+ replacements = {
393
+ placeholder_id: self.replacers[self.placeholder_mapping[placeholder_id]][i]
394
+ for placeholder_id, i in zip(self.placeholder_mapping.keys(), indice)
395
+ }
396
+ return self.template.format(**replacements)
397
+
398
+ def getmany(self, limit: int, offset: int = 0) -> list[str]:
399
+ """返回一组可能性组成的列表,为了避免结果太长导致内存占用超限,使用 limit 限制列表的长度,使用 offset 调整偏移。
400
+ 若 limit 和 offset 的设置导致预期的结果长度超过剩下的实际长度,则会回到开头。
401
+ """
402
+ return [self.getn(n) for n in range(offset, offset + limit)]
403
+
404
+ def total(self) -> int:
405
+ "计算可能性的数目"
406
+ if self.__total is None:
407
+ self.__total = prod(self.placeholder_choices.values())
408
+ return self.__total
409
+
410
+ def __make_template(self, text: str):
411
+ """将输入的提示词转化成符合 python str.format 要求格式的模板,
412
+ 并将 option 和 wildcard 按照顺序在模板中留下 {r0}, {r1} 等占位符,
413
+ 即使遇到相同的 option 或 wildcard,留下的占位符编号也不同,从而使每项都独立变化。
414
+ """
415
+ self.placeholder_mapping = {}
416
+ placeholder_id = 0
417
+ replacer_id = 0
418
+ replacers_rev = {} # replacers => id
419
+ blocks = []
420
+ # 记录所处理过的通配符末尾在文本中的位置,用于拼接完整的模板
421
+ tail = 0
422
+ for match in self.RE_REPLACER.finditer(text):
423
+ # 提取并展开通配符内容
424
+ m = match.group(0)
425
+ if m.startswith("{"):
426
+ choices = expand_options(m[1:-1])
427
+ elif m.startswith("__"):
428
+ keyword = m[2:-2].lower()
429
+ keyword = wildcard_normalize(keyword)
430
+ choices = expand_wildcard(keyword)
431
+ else:
432
+ raise ValueError(f"{m!r} is not a wildcard or option")
433
+
434
+ # 记录通配符的替换项列表和ID,相同的通配符只保留第一个
435
+ if choices not in replacers_rev:
436
+ replacers_rev[choices] = replacer_id
437
+ replacer_id += 1
438
+
439
+ # 拼接通配符前方文本
440
+ start, end = match.span()
441
+ blocks.append(text[tail:start])
442
+ tail = end
443
+ # 将通配符替换为占位符,并记录占位符和替换项列表的索引的映射
444
+ blocks.append(f"{{r{placeholder_id}}}")
445
+ self.placeholder_mapping[f"r{placeholder_id}"] = replacers_rev[choices]
446
+ placeholder_id += 1
447
+
448
+ if tail < len(text):
449
+ blocks.append(text[tail:])
450
+ self.template = "".join(blocks)
451
+ self.replacers = {v: k for k, v in replacers_rev.items()}
452
+ self.placeholder_choices = {
453
+ placeholder_id: len(self.replacers[replacer_id])
454
+ for placeholder_id, replacer_id in self.placeholder_mapping.items()
455
+ }
456
+
457
+
458
+ def test_option():
459
+ text = "{|a|b|c}"
460
+ answer = ["", "a", "b", "c"]
461
+ p = WildcardProcessor(text)
462
+ assert p.total() == len(answer)
463
+ assert p.getn(0) == answer[0]
464
+ assert p.getmany(4) == answer
465
+ assert p.getmany(4, 1) == answer[1:]
466
+
467
+
468
+ def test_same():
469
+ text = "{a|b},{a|b}"
470
+ answer = ["a,a", "b,a", "a,b", "b,b"]
471
+ p = WildcardProcessor(text)
472
+ assert p.total() == len(answer)
473
+ assert p.getn(0) == answer[0]
474
+ assert p.getmany(4) == answer
475
+ assert p.getmany(4, 1) == answer[1:]
476
+
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/xyplot.py ADDED
@@ -0,0 +1,719 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, torch
2
+ from pathlib import Path
3
+ from PIL import Image, ImageDraw, ImageFont
4
+ from .utils import easySave, get_sd_version
5
+ from .adv_encode import advanced_encode
6
+ from .controlnet import easyControlnet
7
+ from .log import log_node_warn
8
+ from ..modules.layer_diffuse import LayerDiffuse
9
+ from ..config import RESOURCES_DIR
10
+ from nodes import CLIPTextEncode
11
+ import pprint
12
+ try:
13
+ from comfy_extras.nodes_flux import FluxGuidance
14
+ except:
15
+ FluxGuidance = None
16
+
17
+ class easyXYPlot():
18
+
19
+ def __init__(self, xyPlotData, save_prefix, image_output, prompt, extra_pnginfo, my_unique_id, sampler, easyCache):
20
+ self.x_node_type, self.x_type = sampler.safe_split(xyPlotData.get("x_axis"), ': ')
21
+ self.y_node_type, self.y_type = sampler.safe_split(xyPlotData.get("y_axis"), ': ')
22
+ self.x_values = xyPlotData.get("x_vals") if self.x_type != "None" else []
23
+ self.y_values = xyPlotData.get("y_vals") if self.y_type != "None" else []
24
+ self.custom_font = xyPlotData.get("custom_font")
25
+
26
+ self.grid_spacing = xyPlotData.get("grid_spacing")
27
+ self.latent_id = 0
28
+ self.output_individuals = xyPlotData.get("output_individuals")
29
+
30
+ self.x_label, self.y_label = [], []
31
+ self.max_width, self.max_height = 0, 0
32
+ self.latents_plot = []
33
+ self.image_list = []
34
+
35
+ self.num_cols = len(self.x_values) if len(self.x_values) > 0 else 1
36
+ self.num_rows = len(self.y_values) if len(self.y_values) > 0 else 1
37
+
38
+ self.total = self.num_cols * self.num_rows
39
+ self.num = 0
40
+
41
+ self.save_prefix = save_prefix
42
+ self.image_output = image_output
43
+ self.prompt = prompt
44
+ self.extra_pnginfo = extra_pnginfo
45
+ self.my_unique_id = my_unique_id
46
+
47
+ self.sampler = sampler
48
+ self.easyCache = easyCache
49
+
50
+ # Helper Functions
51
+ @staticmethod
52
+ def define_variable(plot_image_vars, value_type, value, index):
53
+
54
+ plot_image_vars[value_type] = value
55
+ if value_type in ["seed", "Seeds++ Batch"]:
56
+ value_label = f"seed: {value}"
57
+ else:
58
+ value_label = f"{value_type}: {value}"
59
+
60
+ if "ControlNet" in value_type:
61
+ value_label = f"ControlNet {index + 1}"
62
+
63
+ if value_type in ['Lora', 'Checkpoint']:
64
+ arr = value.split(',')
65
+ model_name = os.path.basename(os.path.splitext(arr[0])[0])
66
+ trigger_words = ' ' + arr[3] if value_type == 'Lora' and len(arr) > 3 and len(arr[3]) > 2 else ''
67
+ lora_weight = float(arr[1]) if value_type == 'Lora' and len(arr) > 1 else 0
68
+ lora_weight_desc = f" w:{lora_weight:.2f}" if value_type == 'Lora' and lora_weight != 1.0 else ''
69
+ value_label = f"{model_name[:25]}{lora_weight_desc}{trigger_words}"
70
+
71
+ if value_type in ["ModelMergeBlocks"]:
72
+ if ":" in value:
73
+ line = value.split(':')
74
+ value_label = f"{line[0]}"
75
+ elif len(value) > 16:
76
+ value_label = f"ModelMergeBlocks {index + 1}"
77
+ else:
78
+ value_label = f"MMB: {value}"
79
+
80
+ if value_type in ["Pos Condition"]:
81
+ value_label = f"pos cond {index + 1}" if index>0 else f"pos cond"
82
+ if value_type in ["Neg Condition"]:
83
+ value_label = f"neg cond {index + 1}" if index>0 else f"neg cond"
84
+
85
+ if value_type in ["Positive Prompt S/R"]:
86
+ value_label = f"pos prompt {index + 1}" if index>0 else f"pos prompt"
87
+ if value_type in ["Negative Prompt S/R"]:
88
+ value_label = f"neg prompt {index + 1}" if index>0 else f"neg prompt"
89
+
90
+ if value_type in ["steps", "cfg", "denoise", "clip_skip",
91
+ "lora_model_strength", "lora_clip_strength"]:
92
+ value_label = f"{value_type}: {value}"
93
+
94
+ if value_type == "positive":
95
+ value_label = f"pos prompt {index + 1}"
96
+ elif value_type == "negative":
97
+ value_label = f"neg prompt {index + 1}"
98
+
99
+ return plot_image_vars, value_label
100
+
101
+ @staticmethod
102
+ def get_font(font_size, font_path=None):
103
+ if font_path is None:
104
+ font_path = str(Path(os.path.join(RESOURCES_DIR, 'OpenSans-Medium.ttf')))
105
+ return ImageFont.truetype(font_path, font_size)
106
+
107
+ @staticmethod
108
+ def update_label(label, value, num_items):
109
+ if len(label) < num_items:
110
+ return [*label, value]
111
+ return label
112
+
113
+ @staticmethod
114
+ def rearrange_tensors(latent, num_cols, num_rows):
115
+ new_latent = []
116
+ for i in range(num_rows):
117
+ for j in range(num_cols):
118
+ index = j * num_rows + i
119
+ new_latent.append(latent[index])
120
+ return new_latent
121
+
122
+ def calculate_background_dimensions(self):
123
+ border_size = int((self.max_width // 8) * 1.5) if self.y_type != "None" or self.x_type != "None" else 0
124
+
125
+ bg_width = self.num_cols * (self.max_width + self.grid_spacing) - self.grid_spacing + border_size * (
126
+ self.y_type != "None")
127
+ bg_height = self.num_rows * (self.max_height + self.grid_spacing) - self.grid_spacing + border_size * (
128
+ self.x_type != "None")
129
+
130
+ # Add space at the bottom of the image for common informaiton about the image
131
+ bg_height = bg_height + (border_size*2)
132
+ # print(f"Grid Size: width = {bg_width} height = {bg_height} border_size = {border_size}")
133
+
134
+ x_offset_initial = border_size if self.y_type != "None" else 0
135
+ y_offset = border_size if self.x_type != "None" else 0
136
+
137
+ return bg_width, bg_height, x_offset_initial, y_offset
138
+
139
+
140
+ def adjust_font_size(self, text, initial_font_size, label_width):
141
+ font = self.get_font(initial_font_size, self.custom_font)
142
+ text_width = font.getbbox(text)
143
+ # pprint.pp(f"Initial font size: {initial_font_size}, text: {text}, text_width: {text_width}")
144
+ if text_width and text_width[2]:
145
+ text_width = text_width[2]
146
+
147
+ scaling_factor = 0.9
148
+ if text_width > (label_width * scaling_factor):
149
+ # print(f"Adjusting font size from {initial_font_size} to fit text width {text_width} into label width {label_width} scaling_factor {scaling_factor}")
150
+ return int(initial_font_size * (label_width / text_width) * scaling_factor)
151
+ else:
152
+ return initial_font_size
153
+
154
+ def textsize(self, d, text, font):
155
+ _, _, width, height = d.textbbox((0, 0), text=text, font=font)
156
+ return width, height
157
+
158
+ def create_label(self, img, text, initial_font_size, is_x_label=True, max_font_size=70, min_font_size=10, label_width=0, label_height=0):
159
+
160
+ # if the label_width is specified, leave it along. Otherwise do the old logic.
161
+ if label_width == 0:
162
+ label_width = img.width if is_x_label else img.height
163
+
164
+ text_lines = text.split('\n')
165
+ longest_line = max(text_lines, key=len)
166
+
167
+ # Adjust font size
168
+ font_size = self.adjust_font_size(longest_line, initial_font_size, label_width)
169
+ font_size = min(max_font_size, font_size) # Ensure font isn't too large
170
+ font_size = max(min_font_size, font_size) # Ensure font isn't too small
171
+
172
+ if label_height == 0:
173
+ label_height = int(font_size * 1.5) if is_x_label else font_size
174
+
175
+ label_bg = Image.new('RGBA', (label_width, label_height), color=(255, 255, 255, 0))
176
+ d = ImageDraw.Draw(label_bg)
177
+
178
+ font = self.get_font(font_size, self.custom_font)
179
+
180
+ # Check if text will fit, if not insert ellipsis and reduce text
181
+ if self.textsize(d, text, font=font)[0] > label_width:
182
+ while self.textsize(d, text + '...', font=font)[0] > label_width and len(text) > 0:
183
+ text = text[:-1]
184
+ text = text + '...'
185
+
186
+ # Compute text width and height for multi-line text
187
+
188
+ text_widths, text_heights = zip(*[self.textsize(d, line, font=font) for line in text_lines])
189
+ max_text_width = max(text_widths)
190
+ total_text_height = sum(text_heights)
191
+
192
+ # Compute position for each line of text
193
+ lines_positions = []
194
+ current_y = 0
195
+ for line, line_width, line_height in zip(text_lines, text_widths, text_heights):
196
+ text_x = (label_width - line_width) // 2
197
+ text_y = current_y + (label_height - total_text_height) // 2
198
+ current_y += line_height
199
+ lines_positions.append((line, (text_x, text_y)))
200
+
201
+ # Draw each line of text
202
+ for line, (text_x, text_y) in lines_positions:
203
+ d.text((text_x, text_y), line, fill='black', font=font)
204
+
205
+ return label_bg
206
+
207
+ def sample_plot_image(self, plot_image_vars, samples, preview_latent, latents_plot, image_list, disable_noise,
208
+ start_step, last_step, force_full_denoise, x_value=None, y_value=None):
209
+ model, clip, vae, positive, negative, seed, steps, cfg = None, None, None, None, None, None, None, None
210
+ sampler_name, scheduler, denoise = None, None, None
211
+
212
+ a1111_prompt_style = plot_image_vars['a1111_prompt_style'] if "a1111_prompt_style" in plot_image_vars else False
213
+ clip = clip if clip is not None else plot_image_vars["clip"]
214
+ steps = plot_image_vars['steps'] if "steps" in plot_image_vars else 1
215
+
216
+ sd_version = get_sd_version(plot_image_vars['model'])
217
+ # 高级用法
218
+ if plot_image_vars["x_node_type"] == "advanced" or plot_image_vars["y_node_type"] == "advanced":
219
+ if self.x_type == "Seeds++ Batch" or self.y_type == "Seeds++ Batch":
220
+ seed = int(x_value) if self.x_type == "Seeds++ Batch" else int(y_value)
221
+ if self.x_type == "Steps" or self.y_type == "Steps":
222
+ steps = int(x_value) if self.x_type == "Steps" else int(y_value)
223
+ if self.x_type == "StartStep" or self.y_type == "StartStep":
224
+ start_step = int(x_value) if self.x_type == "StartStep" else int(y_value)
225
+ if self.x_type == "EndStep" or self.y_type == "EndStep":
226
+ last_step = int(x_value) if self.x_type == "EndStep" else int(y_value)
227
+ if self.x_type == "CFG Scale" or self.y_type == "CFG Scale":
228
+ cfg = float(x_value) if self.x_type == "CFG Scale" else float(y_value)
229
+ if self.x_type == "Sampler" or self.y_type == "Sampler":
230
+ sampler_name = x_value if self.x_type == "Sampler" else y_value
231
+ if self.x_type == "Scheduler" or self.y_type == "Scheduler":
232
+ scheduler = x_value if self.x_type == "Scheduler" else y_value
233
+ if self.x_type == "Sampler&Scheduler" or self.y_type == "Sampler&Scheduler":
234
+ arr = x_value.split(',') if self.x_type == "Sampler&Scheduler" else y_value.split(',')
235
+ if arr[0] and arr[0]!= 'None':
236
+ sampler_name = arr[0]
237
+ if arr[1] and arr[1]!= 'None':
238
+ scheduler = arr[1]
239
+ if self.x_type == "Denoise" or self.y_type == "Denoise":
240
+ denoise = float(x_value) if self.x_type == "Denoise" else float(y_value)
241
+ if self.x_type == "Pos Condition" or self.y_type == "Pos Condition":
242
+ positive = plot_image_vars['positive_cond_stack'][int(x_value)] if self.x_type == "Pos Condition" else plot_image_vars['positive_cond_stack'][int(y_value)]
243
+ if self.x_type == "Neg Condition" or self.y_type == "Neg Condition":
244
+ negative = plot_image_vars['negative_cond_stack'][int(x_value)] if self.x_type == "Neg Condition" else plot_image_vars['negative_cond_stack'][int(y_value)]
245
+ # 模型叠加
246
+ if self.x_type == "ModelMergeBlocks" or self.y_type == "ModelMergeBlocks":
247
+ ckpt_name_1, ckpt_name_2 = plot_image_vars['models']
248
+ model1, clip1, vae1, clip_vision = self.easyCache.load_checkpoint(ckpt_name_1)
249
+ model2, clip2, vae2, clip_vision = self.easyCache.load_checkpoint(ckpt_name_2)
250
+ xy_values = x_value if self.x_type == "ModelMergeBlocks" else y_value
251
+ if ":" in xy_values:
252
+ xy_line = xy_values.split(':')
253
+ xy_values = xy_line[1]
254
+
255
+ xy_arrs = xy_values.split(',')
256
+ # ModelMergeBlocks
257
+ if len(xy_arrs) == 3:
258
+ input, middle, out = xy_arrs
259
+ kwargs = {
260
+ "input": input,
261
+ "middle": middle,
262
+ "out": out
263
+ }
264
+ elif len(xy_arrs) == 30:
265
+ kwargs = {}
266
+ kwargs["time_embed."] = xy_arrs[0]
267
+ kwargs["label_emb."] = xy_arrs[1]
268
+
269
+ for i in range(12):
270
+ kwargs["input_blocks.{}.".format(i)] = xy_arrs[2+i]
271
+
272
+ for i in range(3):
273
+ kwargs["middle_block.{}.".format(i)] = xy_arrs[14+i]
274
+
275
+ for i in range(12):
276
+ kwargs["output_blocks.{}.".format(i)] = xy_arrs[17+i]
277
+
278
+ kwargs["out."] = xy_arrs[29]
279
+ else:
280
+ raise Exception("ModelMergeBlocks weight length error")
281
+ default_ratio = next(iter(kwargs.values()))
282
+
283
+ m = model1.clone()
284
+ kp = model2.get_key_patches("diffusion_model.")
285
+
286
+ for k in kp:
287
+ ratio = float(default_ratio)
288
+ k_unet = k[len("diffusion_model."):]
289
+
290
+ last_arg_size = 0
291
+ for arg in kwargs:
292
+ if k_unet.startswith(arg) and last_arg_size < len(arg):
293
+ ratio = float(kwargs[arg])
294
+ last_arg_size = len(arg)
295
+
296
+ m.add_patches({k: kp[k]}, 1.0 - ratio, ratio)
297
+
298
+ vae_use = plot_image_vars['vae_use']
299
+
300
+ clip = clip2 if vae_use == 'Use Model 2' else clip1
301
+ if vae_use == 'Use Model 2':
302
+ vae = vae2
303
+ elif vae_use == 'Use Model 1':
304
+ vae = vae1
305
+ else:
306
+ vae = self.easyCache.load_vae(vae_use)
307
+ model = m
308
+
309
+ # 如果存在lora_stack叠加lora
310
+ optional_lora_stack = plot_image_vars['lora_stack']
311
+ if optional_lora_stack is not None and optional_lora_stack != []:
312
+ for lora in optional_lora_stack:
313
+ model, clip = self.easyCache.load_lora(lora)
314
+
315
+ # 处理clip
316
+ clip = clip.clone()
317
+ if plot_image_vars['clip_skip'] != 0:
318
+ clip.clip_layer(plot_image_vars['clip_skip'])
319
+
320
+ # CheckPoint
321
+ if self.x_type == "Checkpoint" or self.y_type == "Checkpoint":
322
+ xy_values = x_value if self.x_type == "Checkpoint" else y_value
323
+ ckpt_name, clip_skip, vae_name = xy_values.split(",")
324
+ ckpt_name = ckpt_name.replace('*', ',')
325
+ vae_name = vae_name.replace('*', ',')
326
+ model, clip, vae, clip_vision = self.easyCache.load_checkpoint(ckpt_name)
327
+ if vae_name != 'None':
328
+ vae = self.easyCache.load_vae(vae_name)
329
+
330
+ # 如果存在lora_stack叠加lora
331
+ optional_lora_stack = plot_image_vars['lora_stack']
332
+ if optional_lora_stack is not None and optional_lora_stack != []:
333
+ for lora in optional_lora_stack:
334
+ lora['model'] = model
335
+ lora['clip'] = clip
336
+ model, clip = self.easyCache.load_lora(lora)
337
+
338
+ # 处理clip
339
+ clip = clip.clone()
340
+ if clip_skip != 'None':
341
+ clip.clip_layer(int(clip_skip))
342
+ positive = plot_image_vars['positive']
343
+ negative = plot_image_vars['negative']
344
+ a1111_prompt_style = plot_image_vars['a1111_prompt_style']
345
+ steps = plot_image_vars['steps']
346
+ clip = clip if clip is not None else plot_image_vars["clip"]
347
+ positive = advanced_encode(clip, positive,
348
+ plot_image_vars['positive_token_normalization'],
349
+ plot_image_vars['positive_weight_interpretation'],
350
+ w_max=1.0,
351
+ apply_to_pooled="enable",
352
+ a1111_prompt_style=a1111_prompt_style, steps=steps)
353
+
354
+ negative = advanced_encode(clip, negative,
355
+ plot_image_vars['negative_token_normalization'],
356
+ plot_image_vars['negative_weight_interpretation'],
357
+ w_max=1.0,
358
+ apply_to_pooled="enable",
359
+ a1111_prompt_style=a1111_prompt_style, steps=steps)
360
+ if "positive_cond" in plot_image_vars:
361
+ positive = positive + plot_image_vars["positive_cond"]
362
+ if "negative_cond" in plot_image_vars:
363
+ negative = negative + plot_image_vars["negative_cond"]
364
+
365
+ # Lora
366
+ if self.x_type == "Lora" or self.y_type == "Lora":
367
+ # print(f"Lora: {x_value} {y_value}")
368
+ model = model if model is not None else plot_image_vars["model"]
369
+ clip = clip if clip is not None else plot_image_vars["clip"]
370
+
371
+ # Build lora_stack from both X and Y axes if both are LoRA types
372
+ lora_stack = []
373
+
374
+ # Add X axis LoRA if present
375
+ if self.x_type == "Lora":
376
+ lora_name, lora_model_strength, lora_clip_strength, _ = x_value.split(",")
377
+ lora_stack.append({"lora_name": lora_name, "model": model, "clip": clip, "model_strength": float(lora_model_strength), "clip_strength": float(lora_clip_strength)})
378
+
379
+ # Add Y axis LoRA if present
380
+ if self.y_type == "Lora":
381
+ lora_name, lora_model_strength, lora_clip_strength, _ = y_value.split(",")
382
+ lora_stack.append({"lora_name": lora_name, "model": model, "clip": clip, "model_strength": float(lora_model_strength), "clip_strength": float(lora_clip_strength)})
383
+
384
+ # print(f"new_lora_stack: {new_lora_stack}")
385
+
386
+
387
+ if 'lora_stack' in plot_image_vars:
388
+ lora_stack = lora_stack + plot_image_vars['lora_stack']
389
+
390
+ if lora_stack is not None and lora_stack != []:
391
+ for lora in lora_stack:
392
+ # Each generation of the model, must use the reference to previously created model / clip objects.
393
+ lora['model'] = model
394
+ lora['clip'] = clip
395
+ model, clip = self.easyCache.load_lora(lora)
396
+
397
+ # 提示词
398
+ if "Positive" in self.x_type or "Positive" in self.y_type:
399
+ if self.x_type == 'Positive Prompt S/R' or self.y_type == 'Positive Prompt S/R':
400
+ positive = x_value if self.x_type == "Positive Prompt S/R" else y_value
401
+
402
+ if sd_version == 'flux':
403
+ positive, = CLIPTextEncode().encode(clip, positive)
404
+ else:
405
+ positive = advanced_encode(clip, positive,
406
+ plot_image_vars['positive_token_normalization'],
407
+ plot_image_vars['positive_weight_interpretation'],
408
+ w_max=1.0,
409
+ apply_to_pooled="enable", a1111_prompt_style=a1111_prompt_style, steps=steps)
410
+
411
+ # if "positive_cond" in plot_image_vars:
412
+ # positive = positive + plot_image_vars["positive_cond"]
413
+
414
+ if "Negative" in self.x_type or "Negative" in self.y_type:
415
+ if self.x_type == 'Negative Prompt S/R' or self.y_type == 'Negative Prompt S/R':
416
+ negative = x_value if self.x_type == "Negative Prompt S/R" else y_value
417
+
418
+ if sd_version == 'flux':
419
+ negative, = CLIPTextEncode().encode(clip, negative)
420
+ else:
421
+ negative = advanced_encode(clip, negative,
422
+ plot_image_vars['negative_token_normalization'],
423
+ plot_image_vars['negative_weight_interpretation'],
424
+ w_max=1.0,
425
+ apply_to_pooled="enable", a1111_prompt_style=a1111_prompt_style, steps=steps)
426
+ # if "negative_cond" in plot_image_vars:
427
+ # negative = negative + plot_image_vars["negative_cond"]
428
+
429
+ # ControlNet
430
+ if "ControlNet" in self.x_type or "ControlNet" in self.y_type:
431
+ cnet = plot_image_vars["cnet"] if "cnet" in plot_image_vars else None
432
+ positive = plot_image_vars["positive_cond"] if "positive" in plot_image_vars else None
433
+ negative = plot_image_vars["negative_cond"] if "negative" in plot_image_vars else None
434
+ if cnet:
435
+ index = x_value if "ControlNet" in self.x_type else y_value
436
+ controlnet = cnet[index]
437
+ for index, item in enumerate(controlnet):
438
+ control_net_name = item[0]
439
+ image = item[1]
440
+ strength = item[2]
441
+ start_percent = item[3]
442
+ end_percent = item[4]
443
+ provided_control_net = item[5] if len(item) > 5 else None
444
+ positive, negative = easyControlnet().apply(control_net_name, image, positive, negative, strength, start_percent, end_percent, provided_control_net, 1)
445
+ # Flux guidance
446
+ if self.x_type == "Flux Guidance" or self.y_type == "Flux Guidance":
447
+ positive = plot_image_vars["positive_cond"] if "positive" in plot_image_vars else None
448
+ flux_guidance = float(x_value) if self.x_type == "Flux Guidance" else float(y_value)
449
+ positive, = FluxGuidance().append(positive, flux_guidance)
450
+
451
+ # 简单用法
452
+ if plot_image_vars["x_node_type"] == "loader" or plot_image_vars["y_node_type"] == "loader":
453
+ if self.x_type == 'ckpt_name' or self.y_type == 'ckpt_name':
454
+ ckpt_name = x_value if self.x_type == "ckpt_name" else y_value
455
+ model, clip, vae, clip_vision = self.easyCache.load_checkpoint(ckpt_name)
456
+
457
+ if self.x_type == 'lora_name' or self.y_type == 'lora_name':
458
+ model, clip, vae, clip_vision = self.easyCache.load_checkpoint(plot_image_vars['ckpt_name'])
459
+ lora_name = x_value if self.x_type == "lora_name" else y_value
460
+ lora = {"lora_name": lora_name, "model": model, "clip": clip, "model_strength": 1, "clip_strength": 1}
461
+ model, clip = self.easyCache.load_lora(lora)
462
+
463
+ if self.x_type == 'lora_model_strength' or self.y_type == 'lora_model_strength':
464
+ model, clip, vae, clip_vision = self.easyCache.load_checkpoint(plot_image_vars['ckpt_name'])
465
+ lora_model_strength = float(x_value) if self.x_type == "lora_model_strength" else float(y_value)
466
+ lora = {"lora_name": plot_image_vars['lora_name'], "model": model, "clip": clip, "model_strength": lora_model_strength, "clip_strength": plot_image_vars['lora_clip_strength']}
467
+ model, clip = self.easyCache.load_lora(lora)
468
+
469
+ if self.x_type == 'lora_clip_strength' or self.y_type == 'lora_clip_strength':
470
+ model, clip, vae, clip_vision = self.easyCache.load_checkpoint(plot_image_vars['ckpt_name'])
471
+ lora_clip_strength = float(x_value) if self.x_type == "lora_clip_strength" else float(y_value)
472
+ lora = {"lora_name": plot_image_vars['lora_name'], "model": model, "clip": clip, "model_strength": plot_image_vars['lora_model_strength'], "clip_strength": lora_clip_strength}
473
+ model, clip = self.easyCache.load_lora(lora)
474
+
475
+ # Check for custom VAE
476
+ if self.x_type == 'vae_name' or self.y_type == 'vae_name':
477
+ vae_name = x_value if self.x_type == "vae_name" else y_value
478
+ vae = self.easyCache.load_vae(vae_name)
479
+
480
+ # CLIP skip
481
+ if not clip:
482
+ raise Exception("No CLIP found")
483
+ clip = clip.clone()
484
+ clip.clip_layer(plot_image_vars['clip_skip'])
485
+
486
+ if sd_version == 'flux':
487
+ positive, = CLIPTextEncode().encode(clip, positive)
488
+ else:
489
+ positive = advanced_encode(clip, plot_image_vars['positive'],
490
+ plot_image_vars['positive_token_normalization'],
491
+ plot_image_vars['positive_weight_interpretation'], w_max=1.0,
492
+ apply_to_pooled="enable",a1111_prompt_style=a1111_prompt_style, steps=steps)
493
+
494
+ if sd_version == 'flux':
495
+ negative, = CLIPTextEncode().encode(clip, negative)
496
+ else:
497
+ negative = advanced_encode(clip, plot_image_vars['negative'],
498
+ plot_image_vars['negative_token_normalization'],
499
+ plot_image_vars['negative_weight_interpretation'], w_max=1.0,
500
+ apply_to_pooled="enable", a1111_prompt_style=a1111_prompt_style, steps=steps)
501
+
502
+
503
+ model = model if model is not None else plot_image_vars["model"]
504
+ vae = vae if vae is not None else plot_image_vars["vae"]
505
+ positive = positive if positive is not None else plot_image_vars["positive_cond"]
506
+ negative = negative if negative is not None else plot_image_vars["negative_cond"]
507
+
508
+ seed = seed if seed is not None else plot_image_vars["seed"]
509
+ steps = steps if steps is not None else plot_image_vars["steps"]
510
+ cfg = cfg if cfg is not None else plot_image_vars["cfg"]
511
+ sampler_name = sampler_name if sampler_name is not None else plot_image_vars["sampler_name"]
512
+ scheduler = scheduler if scheduler is not None else plot_image_vars["scheduler"]
513
+ denoise = denoise if denoise is not None else plot_image_vars["denoise"]
514
+
515
+ noise_device = plot_image_vars["noise_device"] if "noise_device" in plot_image_vars else 'cpu'
516
+
517
+ # LayerDiffuse
518
+ layer_diffusion_method = plot_image_vars["layer_diffusion_method"] if "layer_diffusion_method" in plot_image_vars else None
519
+ empty_samples = plot_image_vars["empty_samples"] if "empty_samples" in plot_image_vars else None
520
+
521
+ if layer_diffusion_method:
522
+ samp_blend_samples = plot_image_vars["blend_samples"] if "blend_samples" in plot_image_vars else None
523
+ additional_cond = plot_image_vars["layer_diffusion_cond"] if "layer_diffusion_cond" in plot_image_vars else None
524
+
525
+ images = plot_image_vars["images"].movedim(-1, 1) if "images" in plot_image_vars else None
526
+ weight = plot_image_vars['layer_diffusion_weight'] if 'layer_diffusion_weight' in plot_image_vars else 1.0
527
+ model, positive, negative = LayerDiffuse().apply_layer_diffusion(model, layer_diffusion_method, weight, samples,
528
+ samp_blend_samples, positive,
529
+ negative, images, additional_cond)
530
+
531
+ samples = empty_samples if layer_diffusion_method is not None and empty_samples is not None else samples
532
+ # Sample
533
+ samples = self.sampler.common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, samples,
534
+ denoise=denoise, disable_noise=disable_noise, preview_latent=preview_latent,
535
+ start_step=start_step, last_step=last_step,
536
+ force_full_denoise=force_full_denoise, noise_device=noise_device)
537
+
538
+ # Decode images and store
539
+ latent = samples["samples"]
540
+
541
+ # Add the latent tensor to the tensors list
542
+ latents_plot.append(latent)
543
+
544
+ # Decode the image
545
+ image = vae.decode(latent).cpu()
546
+
547
+ if self.output_individuals in [True, "True"]:
548
+ easySave(image, self.save_prefix, self.image_output)
549
+
550
+ # Convert the image from tensor to PIL Image and add it to the list
551
+ pil_image = self.sampler.tensor2pil(image)
552
+ image_list.append(pil_image)
553
+
554
+ # Update max dimensions
555
+ self.max_width = max(self.max_width, pil_image.width)
556
+ self.max_height = max(self.max_height, pil_image.height)
557
+
558
+ # Return the touched variables
559
+ return image_list, self.max_width, self.max_height, latents_plot
560
+
561
+ # Process Functions
562
+ def validate_xy_plot(self):
563
+ if self.x_type == 'None' and self.y_type == 'None':
564
+ log_node_warn(f'#{self.my_unique_id}','No Valid Plot Types - Reverting to default sampling...')
565
+ return False
566
+ else:
567
+ return True
568
+
569
+ def get_latent(self, samples):
570
+ # Extract the 'samples' tensor from the dictionary
571
+ latent_image_tensor = samples["samples"]
572
+
573
+ # Split the tensor into individual image tensors
574
+ image_tensors = torch.split(latent_image_tensor, 1, dim=0)
575
+
576
+ # Create a list of dictionaries containing the individual image tensors
577
+ latent_list = [{'samples': image} for image in image_tensors]
578
+
579
+ # Set latent only to the first latent of batch
580
+ if self.latent_id >= len(latent_list):
581
+ log_node_warn(f'#{self.my_unique_id}',f'The selected latent_id ({self.latent_id}) is out of range.')
582
+ log_node_warn(f'#{self.my_unique_id}', f'Automatically setting the latent_id to the last image in the list (index: {len(latent_list) - 1}).')
583
+
584
+ self.latent_id = len(latent_list) - 1
585
+
586
+ return latent_list[self.latent_id]
587
+
588
+ def get_labels_and_sample(self, plot_image_vars, latent_image, preview_latent, start_step, last_step,
589
+ force_full_denoise, disable_noise):
590
+ # Handle X-only variation (Y is "None")
591
+ if self.y_type == 'None':
592
+ for x_index, x_value in enumerate(self.x_values):
593
+ plot_image_vars, x_value_label = self.define_variable(plot_image_vars, self.x_type, x_value, x_index)
594
+ self.x_label = self.update_label(self.x_label, x_value_label, len(self.x_values))
595
+
596
+ self.image_list, self.max_width, self.max_height, self.latents_plot = self.sample_plot_image(
597
+ plot_image_vars, latent_image, preview_latent, self.latents_plot, self.image_list,
598
+ disable_noise, start_step, last_step, force_full_denoise, x_value)
599
+ self.num += 1
600
+ # Handle Y-only variation (X is "None")
601
+ elif self.x_type == 'None':
602
+ for y_index, y_value in enumerate(self.y_values):
603
+ plot_image_vars, y_value_label = self.define_variable(plot_image_vars, self.y_type, y_value, y_index)
604
+ self.y_label = self.update_label(self.y_label, y_value_label, len(self.y_values))
605
+
606
+ self.image_list, self.max_width, self.max_height, self.latents_plot = self.sample_plot_image(
607
+ plot_image_vars, latent_image, preview_latent, self.latents_plot, self.image_list,
608
+ disable_noise, start_step, last_step, force_full_denoise, y_value=y_value)
609
+ self.num += 1
610
+ # Handle both X and Y variation
611
+ else:
612
+ for x_index, x_value in enumerate(self.x_values):
613
+ plot_image_vars, x_value_label = self.define_variable(plot_image_vars, self.x_type, x_value, x_index)
614
+ self.x_label = self.update_label(self.x_label, x_value_label, len(self.x_values))
615
+
616
+ for y_index, y_value in enumerate(self.y_values):
617
+ plot_image_vars, y_value_label = self.define_variable(plot_image_vars, self.y_type, y_value, y_index)
618
+ self.y_label = self.update_label(self.y_label, y_value_label, len(self.y_values))
619
+
620
+ self.image_list, self.max_width, self.max_height, self.latents_plot = self.sample_plot_image(
621
+ plot_image_vars, latent_image, preview_latent, self.latents_plot, self.image_list,
622
+ disable_noise, start_step, last_step, force_full_denoise, x_value, y_value)
623
+ self.num += 1
624
+
625
+ # Rearrange latent array to match preview image grid
626
+ self.latents_plot = self.rearrange_tensors(self.latents_plot, self.num_cols, self.num_rows)
627
+
628
+ # Concatenate the tensors along the first dimension (dim=0)
629
+ self.latents_plot = torch.cat(self.latents_plot, dim=0)
630
+
631
+ return self.latents_plot
632
+
633
+ def plot_images_and_labels(self, plot_image_vars):
634
+
635
+ bg_width, bg_height, x_offset_initial, y_offset = self.calculate_background_dimensions()
636
+
637
+ background = Image.new('RGBA', (int(bg_width), int(bg_height)), color=(255, 255, 255, 255))
638
+
639
+ output_image = []
640
+ for row_index in range(self.num_rows):
641
+ x_offset = x_offset_initial
642
+
643
+ for col_index in range(self.num_cols):
644
+ index = col_index * self.num_rows + row_index
645
+ img = self.image_list[index]
646
+ output_image.append(self.sampler.pil2tensor(img))
647
+ background.paste(img, (x_offset, y_offset))
648
+
649
+ # Handle X label
650
+ if row_index == 0 and self.x_type != "None":
651
+ label_bg = self.create_label(img, self.x_label[col_index], int(48 * img.width / 512))
652
+ label_y = (y_offset - label_bg.height) // 2
653
+ background.alpha_composite(label_bg, (x_offset, label_y))
654
+
655
+ # Handle Y label
656
+ if col_index == 0 and self.y_type != "None":
657
+ label_bg = self.create_label(img, self.y_label[row_index], int(48 * img.height / 512), False)
658
+ label_bg = label_bg.rotate(90, expand=True)
659
+
660
+ label_x = (x_offset - label_bg.width) // 2
661
+ label_y = y_offset + (img.height - label_bg.height) // 2
662
+ background.alpha_composite(label_bg, (label_x, label_y))
663
+
664
+ x_offset += img.width + self.grid_spacing
665
+
666
+ y_offset += img.height + self.grid_spacing
667
+
668
+ # lookup used models in the image
669
+ common_label = ""
670
+ # Update to add a function to do the heavy lifting. Parameters are plot_image_vars name, label to use, names of the axis,
671
+
672
+ # pprint.pp(plot_image_vars)
673
+
674
+ # We don't process LORAs here because there can be multiple of them.
675
+ labels = [
676
+ {"id": "ckpt_name", "id_desc": "ckpt", "axis_type" : "Checkpoint"},
677
+ {"id": "vae_name", "id_desc": '', "axis_type" : "vae_name"},
678
+ {"id": "sampler_name", "id_desc": "sampler", "axis_type" : "Sampler"},
679
+ {"id": "scheduler", "id_desc": '', "axis_type" : "Scheduler"},
680
+ {"id": "steps", "id_desc": '', "axis_type" : "Steps"},
681
+ {"id": "Flux Guidance", "id_desc": 'guidance', "axis_type" : "Flux Guidance"},
682
+ {"id": "seed", "id_desc": '', "axis_type" : "Seeds++ Batch"}
683
+ ]
684
+
685
+ for item in labels:
686
+ # Only add the label if it's not one of the axis
687
+ # print(f"Checking item: {item['id']} axis_type {item['axis_type']} x_type: {self.x_type} y_type: {self.y_type}")
688
+ if self.x_type != item['axis_type'] and self.y_type != item['axis_type']:
689
+ common_label += self.add_common_label(item['id'], plot_image_vars, item['id_desc'])
690
+ common_label += f"\n"
691
+
692
+ if plot_image_vars['lora_stack'] is not None and plot_image_vars['lora_stack'] != []:
693
+ # print(f"lora_stack: {plot_image_vars['lora_stack']}")
694
+ for lora in plot_image_vars['lora_stack']:
695
+
696
+ lora_name = lora['lora_name']
697
+ lora_weight = lora['model_strength']
698
+ if lora_name is not None and len(lora_name) > 0 and lora_weight > 0:
699
+ common_label += f"LORA: {lora_name} weight: {lora_weight:.2f} \n"
700
+
701
+ common_label = common_label.strip()
702
+
703
+ if len(common_label) > 0:
704
+ label_height = background.height - y_offset
705
+ label_bg = self.create_label(background, common_label, int(48 * background.width / 512), label_width=background.width, label_height=label_height)
706
+ label_x = (background.width - label_bg.width) // 2
707
+ label_y = y_offset
708
+ # print(f"Adding common label: {common_label} x = {label_x} y = {label_y}")
709
+ background.alpha_composite(label_bg, (label_x, label_y))
710
+
711
+ return (self.sampler.pil2tensor(background), output_image)
712
+
713
+ def add_common_label(self, tag, plot_image_vars, description = ''):
714
+ label = ''
715
+ if description == '': description = tag
716
+ if tag in plot_image_vars and plot_image_vars[tag] is not None and plot_image_vars[tag] != 'None':
717
+ label += f"{description}: {plot_image_vars[tag]} "
718
+ # print(f"add_common_label: {tag} description: {description} label: {label}" )
719
+ return label
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/ben/model.py ADDED
@@ -0,0 +1,1310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from einops import rearrange
6
+ import torch.utils.checkpoint as checkpoint
7
+ import numpy as np
8
+ from timm.models.layers import DropPath, to_2tuple, trunc_normal_
9
+ from PIL import Image, ImageOps
10
+ from torchvision import transforms
11
+ import numpy as np
12
+ import random
13
+ import cv2
14
+ import os
15
+ import subprocess
16
+ import time
17
+ import tempfile
18
+
19
+
20
+ def set_random_seed(seed):
21
+ random.seed(seed)
22
+ np.random.seed(seed)
23
+ torch.manual_seed(seed)
24
+ torch.cuda.manual_seed(seed)
25
+ torch.cuda.manual_seed_all(seed)
26
+ torch.backends.cudnn.deterministic = True
27
+ torch.backends.cudnn.benchmark = False
28
+
29
+
30
+ set_random_seed(9)
31
+
32
+ torch.set_float32_matmul_precision('highest')
33
+
34
+
35
+ class Mlp(nn.Module):
36
+ """ Multilayer perceptron."""
37
+
38
+ def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
39
+ super().__init__()
40
+ out_features = out_features or in_features
41
+ hidden_features = hidden_features or in_features
42
+ self.fc1 = nn.Linear(in_features, hidden_features)
43
+ self.act = act_layer()
44
+ self.fc2 = nn.Linear(hidden_features, out_features)
45
+ self.drop = nn.Dropout(drop)
46
+
47
+ def forward(self, x):
48
+ x = self.fc1(x)
49
+ x = self.act(x)
50
+ x = self.drop(x)
51
+ x = self.fc2(x)
52
+ x = self.drop(x)
53
+ return x
54
+
55
+
56
+ def window_partition(x, window_size):
57
+ """
58
+ Args:
59
+ x: (B, H, W, C)
60
+ window_size (int): window size
61
+ Returns:
62
+ windows: (num_windows*B, window_size, window_size, C)
63
+ """
64
+ B, H, W, C = x.shape
65
+ x = x.view(B, H // window_size, window_size, W // window_size, window_size, C)
66
+ windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C)
67
+ return windows
68
+
69
+
70
+ def window_reverse(windows, window_size, H, W):
71
+ """
72
+ Args:
73
+ windows: (num_windows*B, window_size, window_size, C)
74
+ window_size (int): Window size
75
+ H (int): Height of image
76
+ W (int): Width of image
77
+ Returns:
78
+ x: (B, H, W, C)
79
+ """
80
+ B = int(windows.shape[0] / (H * W / window_size / window_size))
81
+ x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1)
82
+ x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1)
83
+ return x
84
+
85
+
86
+ class WindowAttention(nn.Module):
87
+ """ Window based multi-head self attention (W-MSA) module with relative position bias.
88
+ It supports both of shifted and non-shifted window.
89
+ Args:
90
+ dim (int): Number of input channels.
91
+ window_size (tuple[int]): The height and width of the window.
92
+ num_heads (int): Number of attention heads.
93
+ qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True
94
+ qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set
95
+ attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0
96
+ proj_drop (float, optional): Dropout ratio of output. Default: 0.0
97
+ """
98
+
99
+ def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.):
100
+
101
+ super().__init__()
102
+ self.dim = dim
103
+ self.window_size = window_size # Wh, Ww
104
+ self.num_heads = num_heads
105
+ head_dim = dim // num_heads
106
+ self.scale = qk_scale or head_dim ** -0.5
107
+
108
+ # define a parameter table of relative position bias
109
+ self.relative_position_bias_table = nn.Parameter(
110
+ torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads)) # 2*Wh-1 * 2*Ww-1, nH
111
+
112
+ # get pair-wise relative position index for each token inside the window
113
+ coords_h = torch.arange(self.window_size[0])
114
+ coords_w = torch.arange(self.window_size[1])
115
+ coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww
116
+ coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww
117
+ relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww
118
+ relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2
119
+ relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0
120
+ relative_coords[:, :, 1] += self.window_size[1] - 1
121
+ relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1
122
+ relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww
123
+ self.register_buffer("relative_position_index", relative_position_index)
124
+
125
+ self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
126
+ self.attn_drop = nn.Dropout(attn_drop)
127
+ self.proj = nn.Linear(dim, dim)
128
+ self.proj_drop = nn.Dropout(proj_drop)
129
+
130
+ trunc_normal_(self.relative_position_bias_table, std=.02)
131
+ self.softmax = nn.Softmax(dim=-1)
132
+
133
+ def forward(self, x, mask=None):
134
+ """ Forward function.
135
+ Args:
136
+ x: input features with shape of (num_windows*B, N, C)
137
+ mask: (0/-inf) mask with shape of (num_windows, Wh*Ww, Wh*Ww) or None
138
+ """
139
+ B_, N, C = x.shape
140
+ qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
141
+ q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
142
+
143
+ q = q * self.scale
144
+ attn = (q @ k.transpose(-2, -1))
145
+
146
+ relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(
147
+ self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1) # Wh*Ww,Wh*Ww,nH
148
+ relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww
149
+ attn = attn + relative_position_bias.unsqueeze(0)
150
+
151
+ if mask is not None:
152
+ nW = mask.shape[0]
153
+ attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0)
154
+ attn = attn.view(-1, self.num_heads, N, N)
155
+ attn = self.softmax(attn)
156
+ else:
157
+ attn = self.softmax(attn)
158
+
159
+ attn = self.attn_drop(attn)
160
+
161
+ x = (attn @ v).transpose(1, 2).reshape(B_, N, C)
162
+ x = self.proj(x)
163
+ x = self.proj_drop(x)
164
+ return x
165
+
166
+
167
+ class SwinTransformerBlock(nn.Module):
168
+ """ Swin Transformer Block.
169
+ Args:
170
+ dim (int): Number of input channels.
171
+ num_heads (int): Number of attention heads.
172
+ window_size (int): Window size.
173
+ shift_size (int): Shift size for SW-MSA.
174
+ mlp_ratio (float): Ratio of mlp hidden dim to embedding dim.
175
+ qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True
176
+ qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set.
177
+ drop (float, optional): Dropout rate. Default: 0.0
178
+ attn_drop (float, optional): Attention dropout rate. Default: 0.0
179
+ drop_path (float, optional): Stochastic depth rate. Default: 0.0
180
+ act_layer (nn.Module, optional): Activation layer. Default: nn.GELU
181
+ norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
182
+ """
183
+
184
+ def __init__(self, dim, num_heads, window_size=7, shift_size=0,
185
+ mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0.,
186
+ act_layer=nn.GELU, norm_layer=nn.LayerNorm):
187
+ super().__init__()
188
+ self.dim = dim
189
+ self.num_heads = num_heads
190
+ self.window_size = window_size
191
+ self.shift_size = shift_size
192
+ self.mlp_ratio = mlp_ratio
193
+ assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size"
194
+
195
+ self.norm1 = norm_layer(dim)
196
+ self.attn = WindowAttention(
197
+ dim, window_size=to_2tuple(self.window_size), num_heads=num_heads,
198
+ qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop)
199
+
200
+ self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
201
+ self.norm2 = norm_layer(dim)
202
+ mlp_hidden_dim = int(dim * mlp_ratio)
203
+ self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)
204
+
205
+ self.H = None
206
+ self.W = None
207
+
208
+ def forward(self, x, mask_matrix):
209
+ """ Forward function.
210
+ Args:
211
+ x: Input feature, tensor size (B, H*W, C).
212
+ H, W: Spatial resolution of the input feature.
213
+ mask_matrix: Attention mask for cyclic shift.
214
+ """
215
+ B, L, C = x.shape
216
+ H, W = self.H, self.W
217
+ assert L == H * W, "input feature has wrong size"
218
+
219
+ shortcut = x
220
+ x = self.norm1(x)
221
+ x = x.view(B, H, W, C)
222
+
223
+ # pad feature maps to multiples of window size
224
+ pad_l = pad_t = 0
225
+ pad_r = (self.window_size - W % self.window_size) % self.window_size
226
+ pad_b = (self.window_size - H % self.window_size) % self.window_size
227
+ x = F.pad(x, (0, 0, pad_l, pad_r, pad_t, pad_b))
228
+ _, Hp, Wp, _ = x.shape
229
+
230
+ # cyclic shift
231
+ if self.shift_size > 0:
232
+ shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2))
233
+ attn_mask = mask_matrix
234
+ else:
235
+ shifted_x = x
236
+ attn_mask = None
237
+
238
+ # partition windows
239
+ x_windows = window_partition(shifted_x, self.window_size) # nW*B, window_size, window_size, C
240
+ x_windows = x_windows.view(-1, self.window_size * self.window_size, C) # nW*B, window_size*window_size, C
241
+
242
+ # W-MSA/SW-MSA
243
+ attn_windows = self.attn(x_windows, mask=attn_mask) # nW*B, window_size*window_size, C
244
+
245
+ # merge windows
246
+ attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C)
247
+ shifted_x = window_reverse(attn_windows, self.window_size, Hp, Wp) # B H' W' C
248
+
249
+ # reverse cyclic shift
250
+ if self.shift_size > 0:
251
+ x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2))
252
+ else:
253
+ x = shifted_x
254
+
255
+ if pad_r > 0 or pad_b > 0:
256
+ x = x[:, :H, :W, :].contiguous()
257
+
258
+ x = x.view(B, H * W, C)
259
+
260
+ # FFN
261
+ x = shortcut + self.drop_path(x)
262
+ x = x + self.drop_path(self.mlp(self.norm2(x)))
263
+
264
+ return x
265
+
266
+
267
+ class PatchMerging(nn.Module):
268
+ """ Patch Merging Layer
269
+ Args:
270
+ dim (int): Number of input channels.
271
+ norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
272
+ """
273
+
274
+ def __init__(self, dim, norm_layer=nn.LayerNorm):
275
+ super().__init__()
276
+ self.dim = dim
277
+ self.reduction = nn.Linear(4 * dim, 2 * dim, bias=False)
278
+ self.norm = norm_layer(4 * dim)
279
+
280
+ def forward(self, x, H, W):
281
+ """ Forward function.
282
+ Args:
283
+ x: Input feature, tensor size (B, H*W, C).
284
+ H, W: Spatial resolution of the input feature.
285
+ """
286
+ B, L, C = x.shape
287
+ assert L == H * W, "input feature has wrong size"
288
+
289
+ x = x.view(B, H, W, C)
290
+
291
+ # padding
292
+ pad_input = (H % 2 == 1) or (W % 2 == 1)
293
+ if pad_input:
294
+ x = F.pad(x, (0, 0, 0, W % 2, 0, H % 2))
295
+
296
+ x0 = x[:, 0::2, 0::2, :] # B H/2 W/2 C
297
+ x1 = x[:, 1::2, 0::2, :] # B H/2 W/2 C
298
+ x2 = x[:, 0::2, 1::2, :] # B H/2 W/2 C
299
+ x3 = x[:, 1::2, 1::2, :] # B H/2 W/2 C
300
+ x = torch.cat([x0, x1, x2, x3], -1) # B H/2 W/2 4*C
301
+ x = x.view(B, -1, 4 * C) # B H/2*W/2 4*C
302
+
303
+ x = self.norm(x)
304
+ x = self.reduction(x)
305
+
306
+ return x
307
+
308
+
309
+ class BasicLayer(nn.Module):
310
+ """ A basic Swin Transformer layer for one stage.
311
+ Args:
312
+ dim (int): Number of feature channels
313
+ depth (int): Depths of this stage.
314
+ num_heads (int): Number of attention head.
315
+ window_size (int): Local window size. Default: 7.
316
+ mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4.
317
+ qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True
318
+ qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set.
319
+ drop (float, optional): Dropout rate. Default: 0.0
320
+ attn_drop (float, optional): Attention dropout rate. Default: 0.0
321
+ drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0
322
+ norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
323
+ downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None
324
+ use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False.
325
+ """
326
+
327
+ def __init__(self,
328
+ dim,
329
+ depth,
330
+ num_heads,
331
+ window_size=7,
332
+ mlp_ratio=4.,
333
+ qkv_bias=True,
334
+ qk_scale=None,
335
+ drop=0.,
336
+ attn_drop=0.,
337
+ drop_path=0.,
338
+ norm_layer=nn.LayerNorm,
339
+ downsample=None,
340
+ use_checkpoint=False):
341
+ super().__init__()
342
+ self.window_size = window_size
343
+ self.shift_size = window_size // 2
344
+ self.depth = depth
345
+ self.use_checkpoint = use_checkpoint
346
+
347
+ # build blocks
348
+ self.blocks = nn.ModuleList([
349
+ SwinTransformerBlock(
350
+ dim=dim,
351
+ num_heads=num_heads,
352
+ window_size=window_size,
353
+ shift_size=0 if (i % 2 == 0) else window_size // 2,
354
+ mlp_ratio=mlp_ratio,
355
+ qkv_bias=qkv_bias,
356
+ qk_scale=qk_scale,
357
+ drop=drop,
358
+ attn_drop=attn_drop,
359
+ drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path,
360
+ norm_layer=norm_layer)
361
+ for i in range(depth)])
362
+
363
+ # patch merging layer
364
+ if downsample is not None:
365
+ self.downsample = downsample(dim=dim, norm_layer=norm_layer)
366
+ else:
367
+ self.downsample = None
368
+
369
+ def forward(self, x, H, W):
370
+ """ Forward function.
371
+ Args:
372
+ x: Input feature, tensor size (B, H*W, C).
373
+ H, W: Spatial resolution of the input feature.
374
+ """
375
+
376
+ # calculate attention mask for SW-MSA
377
+ Hp = int(np.ceil(H / self.window_size)) * self.window_size
378
+ Wp = int(np.ceil(W / self.window_size)) * self.window_size
379
+ img_mask = torch.zeros((1, Hp, Wp, 1), device=x.device) # 1 Hp Wp 1
380
+ h_slices = (slice(0, -self.window_size),
381
+ slice(-self.window_size, -self.shift_size),
382
+ slice(-self.shift_size, None))
383
+ w_slices = (slice(0, -self.window_size),
384
+ slice(-self.window_size, -self.shift_size),
385
+ slice(-self.shift_size, None))
386
+ cnt = 0
387
+ for h in h_slices:
388
+ for w in w_slices:
389
+ img_mask[:, h, w, :] = cnt
390
+ cnt += 1
391
+
392
+ mask_windows = window_partition(img_mask, self.window_size) # nW, window_size, window_size, 1
393
+ mask_windows = mask_windows.view(-1, self.window_size * self.window_size)
394
+ attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
395
+ attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0))
396
+
397
+ for blk in self.blocks:
398
+ blk.H, blk.W = H, W
399
+ if self.use_checkpoint:
400
+ x = checkpoint.checkpoint(blk, x, attn_mask)
401
+ else:
402
+ x = blk(x, attn_mask)
403
+ if self.downsample is not None:
404
+ x_down = self.downsample(x, H, W)
405
+ Wh, Ww = (H + 1) // 2, (W + 1) // 2
406
+ return x, H, W, x_down, Wh, Ww
407
+ else:
408
+ return x, H, W, x, H, W
409
+
410
+
411
+ class PatchEmbed(nn.Module):
412
+ """ Image to Patch Embedding
413
+ Args:
414
+ patch_size (int): Patch token size. Default: 4.
415
+ in_chans (int): Number of input image channels. Default: 3.
416
+ embed_dim (int): Number of linear projection output channels. Default: 96.
417
+ norm_layer (nn.Module, optional): Normalization layer. Default: None
418
+ """
419
+
420
+ def __init__(self, patch_size=4, in_chans=3, embed_dim=96, norm_layer=None):
421
+ super().__init__()
422
+ patch_size = to_2tuple(patch_size)
423
+ self.patch_size = patch_size
424
+
425
+ self.in_chans = in_chans
426
+ self.embed_dim = embed_dim
427
+
428
+ self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
429
+ if norm_layer is not None:
430
+ self.norm = norm_layer(embed_dim)
431
+ else:
432
+ self.norm = None
433
+
434
+ def forward(self, x):
435
+ """Forward function."""
436
+ # padding
437
+ _, _, H, W = x.size()
438
+ if W % self.patch_size[1] != 0:
439
+ x = F.pad(x, (0, self.patch_size[1] - W % self.patch_size[1]))
440
+ if H % self.patch_size[0] != 0:
441
+ x = F.pad(x, (0, 0, 0, self.patch_size[0] - H % self.patch_size[0]))
442
+
443
+ x = self.proj(x) # B C Wh Ww
444
+ if self.norm is not None:
445
+ Wh, Ww = x.size(2), x.size(3)
446
+ x = x.flatten(2).transpose(1, 2)
447
+ x = self.norm(x)
448
+ x = x.transpose(1, 2).view(-1, self.embed_dim, Wh, Ww)
449
+
450
+ return x
451
+
452
+
453
+ class SwinTransformer(nn.Module):
454
+ """ Swin Transformer backbone.
455
+ A PyTorch impl of : `Swin Transformer: Hierarchical Vision Transformer using Shifted Windows` -
456
+ https://arxiv.org/pdf/2103.14030
457
+ Args:
458
+ pretrain_img_size (int): Input image size for training the pretrained model,
459
+ used in absolute postion embedding. Default 224.
460
+ patch_size (int | tuple(int)): Patch size. Default: 4.
461
+ in_chans (int): Number of input image channels. Default: 3.
462
+ embed_dim (int): Number of linear projection output channels. Default: 96.
463
+ depths (tuple[int]): Depths of each Swin Transformer stage.
464
+ num_heads (tuple[int]): Number of attention head of each stage.
465
+ window_size (int): Window size. Default: 7.
466
+ mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4.
467
+ qkv_bias (bool): If True, add a learnable bias to query, key, value. Default: True
468
+ qk_scale (float): Override default qk scale of head_dim ** -0.5 if set.
469
+ drop_rate (float): Dropout rate.
470
+ attn_drop_rate (float): Attention dropout rate. Default: 0.
471
+ drop_path_rate (float): Stochastic depth rate. Default: 0.2.
472
+ norm_layer (nn.Module): Normalization layer. Default: nn.LayerNorm.
473
+ ape (bool): If True, add absolute position embedding to the patch embedding. Default: False.
474
+ patch_norm (bool): If True, add normalization after patch embedding. Default: True.
475
+ out_indices (Sequence[int]): Output from which stages.
476
+ frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
477
+ -1 means not freezing any parameters.
478
+ use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False.
479
+ """
480
+
481
+ def __init__(self,
482
+ pretrain_img_size=224,
483
+ patch_size=4,
484
+ in_chans=3,
485
+ embed_dim=96,
486
+ depths=[2, 2, 6, 2],
487
+ num_heads=[3, 6, 12, 24],
488
+ window_size=7,
489
+ mlp_ratio=4.,
490
+ qkv_bias=True,
491
+ qk_scale=None,
492
+ drop_rate=0.,
493
+ attn_drop_rate=0.,
494
+ drop_path_rate=0.2,
495
+ norm_layer=nn.LayerNorm,
496
+ ape=False,
497
+ patch_norm=True,
498
+ out_indices=(0, 1, 2, 3),
499
+ frozen_stages=-1,
500
+ use_checkpoint=False):
501
+ super().__init__()
502
+
503
+ self.pretrain_img_size = pretrain_img_size
504
+ self.num_layers = len(depths)
505
+ self.embed_dim = embed_dim
506
+ self.ape = ape
507
+ self.patch_norm = patch_norm
508
+ self.out_indices = out_indices
509
+ self.frozen_stages = frozen_stages
510
+
511
+ # split image into non-overlapping patches
512
+ self.patch_embed = PatchEmbed(
513
+ patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim,
514
+ norm_layer=norm_layer if self.patch_norm else None)
515
+
516
+ # absolute position embedding
517
+ if self.ape:
518
+ pretrain_img_size = to_2tuple(pretrain_img_size)
519
+ patch_size = to_2tuple(patch_size)
520
+ patches_resolution = [pretrain_img_size[0] // patch_size[0], pretrain_img_size[1] // patch_size[1]]
521
+
522
+ self.absolute_pos_embed = nn.Parameter(
523
+ torch.zeros(1, embed_dim, patches_resolution[0], patches_resolution[1]))
524
+ trunc_normal_(self.absolute_pos_embed, std=.02)
525
+
526
+ self.pos_drop = nn.Dropout(p=drop_rate)
527
+
528
+ # stochastic depth
529
+ dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] # stochastic depth decay rule
530
+
531
+ # build layers
532
+ self.layers = nn.ModuleList()
533
+ for i_layer in range(self.num_layers):
534
+ layer = BasicLayer(
535
+ dim=int(embed_dim * 2 ** i_layer),
536
+ depth=depths[i_layer],
537
+ num_heads=num_heads[i_layer],
538
+ window_size=window_size,
539
+ mlp_ratio=mlp_ratio,
540
+ qkv_bias=qkv_bias,
541
+ qk_scale=qk_scale,
542
+ drop=drop_rate,
543
+ attn_drop=attn_drop_rate,
544
+ drop_path=dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])],
545
+ norm_layer=norm_layer,
546
+ downsample=PatchMerging if (i_layer < self.num_layers - 1) else None,
547
+ use_checkpoint=use_checkpoint)
548
+ self.layers.append(layer)
549
+
550
+ num_features = [int(embed_dim * 2 ** i) for i in range(self.num_layers)]
551
+ self.num_features = num_features
552
+
553
+ # add a norm layer for each output
554
+ for i_layer in out_indices:
555
+ layer = norm_layer(num_features[i_layer])
556
+ layer_name = f'norm{i_layer}'
557
+ self.add_module(layer_name, layer)
558
+
559
+ self._freeze_stages()
560
+
561
+ def _freeze_stages(self):
562
+ if self.frozen_stages >= 0:
563
+ self.patch_embed.eval()
564
+ for param in self.patch_embed.parameters():
565
+ param.requires_grad = False
566
+
567
+ if self.frozen_stages >= 1 and self.ape:
568
+ self.absolute_pos_embed.requires_grad = False
569
+
570
+ if self.frozen_stages >= 2:
571
+ self.pos_drop.eval()
572
+ for i in range(0, self.frozen_stages - 1):
573
+ m = self.layers[i]
574
+ m.eval()
575
+ for param in m.parameters():
576
+ param.requires_grad = False
577
+
578
+ def forward(self, x):
579
+
580
+ x = self.patch_embed(x)
581
+
582
+ Wh, Ww = x.size(2), x.size(3)
583
+ if self.ape:
584
+ # interpolate the position embedding to the corresponding size
585
+ absolute_pos_embed = F.interpolate(self.absolute_pos_embed, size=(Wh, Ww), mode='bicubic')
586
+ x = (x + absolute_pos_embed) # B Wh*Ww C
587
+
588
+ outs = [x.contiguous()]
589
+ x = x.flatten(2).transpose(1, 2)
590
+ x = self.pos_drop(x)
591
+
592
+ for i in range(self.num_layers):
593
+ layer = self.layers[i]
594
+ x_out, H, W, x, Wh, Ww = layer(x, Wh, Ww)
595
+
596
+ if i in self.out_indices:
597
+ norm_layer = getattr(self, f'norm{i}')
598
+ x_out = norm_layer(x_out)
599
+
600
+ out = x_out.view(-1, H, W, self.num_features[i]).permute(0, 3, 1, 2).contiguous()
601
+ outs.append(out)
602
+
603
+ return tuple(outs)
604
+
605
+
606
+ def get_activation_fn(activation):
607
+ """Return an activation function given a string"""
608
+ if activation == "gelu":
609
+ return F.gelu
610
+
611
+ raise RuntimeError(F"activation should be gelu, not {activation}.")
612
+
613
+
614
+ def make_cbr(in_dim, out_dim):
615
+ return nn.Sequential(nn.Conv2d(in_dim, out_dim, kernel_size=3, padding=1), nn.InstanceNorm2d(out_dim), nn.GELU())
616
+
617
+
618
+ def make_cbg(in_dim, out_dim):
619
+ return nn.Sequential(nn.Conv2d(in_dim, out_dim, kernel_size=3, padding=1), nn.InstanceNorm2d(out_dim), nn.GELU())
620
+
621
+
622
+ def rescale_to(x, scale_factor: float = 2, interpolation='nearest'):
623
+ return F.interpolate(x, scale_factor=scale_factor, mode=interpolation)
624
+
625
+
626
+ def resize_as(x, y, interpolation='bilinear'):
627
+ return F.interpolate(x, size=y.shape[-2:], mode=interpolation)
628
+
629
+
630
+ def image2patches(x):
631
+ """b c (hg h) (wg w) -> (hg wg b) c h w"""
632
+ x = rearrange(x, 'b c (hg h) (wg w) -> (hg wg b) c h w', hg=2, wg=2)
633
+ return x
634
+
635
+
636
+ def patches2image(x):
637
+ """(hg wg b) c h w -> b c (hg h) (wg w)"""
638
+ x = rearrange(x, '(hg wg b) c h w -> b c (hg h) (wg w)', hg=2, wg=2)
639
+ return x
640
+
641
+
642
+ class PositionEmbeddingSine:
643
+ def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None):
644
+ super().__init__()
645
+ self.num_pos_feats = num_pos_feats
646
+ self.temperature = temperature
647
+ self.normalize = normalize
648
+ if scale is not None and normalize is False:
649
+ raise ValueError("normalize should be True if scale is passed")
650
+ if scale is None:
651
+ scale = 2 * math.pi
652
+ self.scale = scale
653
+ self.dim_t = torch.arange(0, self.num_pos_feats, dtype=torch.float32)
654
+
655
+ def __call__(self, b, h, w):
656
+ device = self.dim_t.device
657
+ mask = torch.zeros([b, h, w], dtype=torch.bool, device=device)
658
+ assert mask is not None
659
+ not_mask = ~mask
660
+ y_embed = not_mask.cumsum(dim=1, dtype=torch.float32)
661
+ x_embed = not_mask.cumsum(dim=2, dtype=torch.float32)
662
+ if self.normalize:
663
+ eps = 1e-6
664
+ y_embed = (y_embed - 0.5) / (y_embed[:, -1:, :] + eps) * self.scale
665
+ x_embed = (x_embed - 0.5) / (x_embed[:, :, -1:] + eps) * self.scale
666
+
667
+ dim_t = self.temperature ** (2 * (self.dim_t.to(device) // 2) / self.num_pos_feats)
668
+ pos_x = x_embed[:, :, :, None] / dim_t
669
+ pos_y = y_embed[:, :, :, None] / dim_t
670
+
671
+ pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
672
+ pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
673
+
674
+ return torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
675
+
676
+
677
+ class PositionEmbeddingSine:
678
+ def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None):
679
+ super().__init__()
680
+ self.num_pos_feats = num_pos_feats
681
+ self.temperature = temperature
682
+ self.normalize = normalize
683
+ if scale is not None and normalize is False:
684
+ raise ValueError("normalize should be True if scale is passed")
685
+ if scale is None:
686
+ scale = 2 * math.pi
687
+ self.scale = scale
688
+ self.dim_t = torch.arange(0, self.num_pos_feats, dtype=torch.float32)
689
+
690
+ def __call__(self, b, h, w):
691
+ device = self.dim_t.device
692
+ mask = torch.zeros([b, h, w], dtype=torch.bool, device=device)
693
+ assert mask is not None
694
+ not_mask = ~mask
695
+ y_embed = not_mask.cumsum(dim=1, dtype=torch.float32)
696
+ x_embed = not_mask.cumsum(dim=2, dtype=torch.float32)
697
+ if self.normalize:
698
+ eps = 1e-6
699
+ y_embed = (y_embed - 0.5) / (y_embed[:, -1:, :] + eps) * self.scale
700
+ x_embed = (x_embed - 0.5) / (x_embed[:, :, -1:] + eps) * self.scale
701
+
702
+ dim_t = self.temperature ** (2 * (self.dim_t.to(device) // 2) / self.num_pos_feats)
703
+ pos_x = x_embed[:, :, :, None] / dim_t
704
+ pos_y = y_embed[:, :, :, None] / dim_t
705
+
706
+ pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
707
+ pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
708
+
709
+ return torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
710
+
711
+
712
+ class MCLM(nn.Module):
713
+ def __init__(self, d_model, num_heads, pool_ratios=[1, 4, 8]):
714
+ super(MCLM, self).__init__()
715
+ self.attention = nn.ModuleList([
716
+ nn.MultiheadAttention(d_model, num_heads, dropout=0.1),
717
+ nn.MultiheadAttention(d_model, num_heads, dropout=0.1),
718
+ nn.MultiheadAttention(d_model, num_heads, dropout=0.1),
719
+ nn.MultiheadAttention(d_model, num_heads, dropout=0.1),
720
+ nn.MultiheadAttention(d_model, num_heads, dropout=0.1)
721
+ ])
722
+
723
+ self.linear1 = nn.Linear(d_model, d_model * 2)
724
+ self.linear2 = nn.Linear(d_model * 2, d_model)
725
+ self.linear3 = nn.Linear(d_model, d_model * 2)
726
+ self.linear4 = nn.Linear(d_model * 2, d_model)
727
+ self.norm1 = nn.LayerNorm(d_model)
728
+ self.norm2 = nn.LayerNorm(d_model)
729
+ self.dropout = nn.Dropout(0.1)
730
+ self.dropout1 = nn.Dropout(0.1)
731
+ self.dropout2 = nn.Dropout(0.1)
732
+ self.activation = get_activation_fn('gelu')
733
+ self.pool_ratios = pool_ratios
734
+ self.p_poses = []
735
+ self.g_pos = None
736
+ self.positional_encoding = PositionEmbeddingSine(num_pos_feats=d_model // 2, normalize=True)
737
+
738
+ def forward(self, l, g):
739
+ """
740
+ l: 4,c,h,w
741
+ g: 1,c,h,w
742
+ """
743
+ self.p_poses = []
744
+ self.g_pos = None
745
+ b, c, h, w = l.size()
746
+ # 4,c,h,w -> 1,c,2h,2w
747
+ concated_locs = rearrange(l, '(hg wg b) c h w -> b c (hg h) (wg w)', hg=2, wg=2)
748
+
749
+ pools = []
750
+ for pool_ratio in self.pool_ratios:
751
+ # b,c,h,w
752
+ tgt_hw = (round(h / pool_ratio), round(w / pool_ratio))
753
+ pool = F.adaptive_avg_pool2d(concated_locs, tgt_hw)
754
+ pools.append(rearrange(pool, 'b c h w -> (h w) b c'))
755
+ if self.g_pos is None:
756
+ pos_emb = self.positional_encoding(pool.shape[0], pool.shape[2], pool.shape[3])
757
+ pos_emb = rearrange(pos_emb, 'b c h w -> (h w) b c')
758
+ self.p_poses.append(pos_emb)
759
+ pools = torch.cat(pools, 0)
760
+ if self.g_pos is None:
761
+ self.p_poses = torch.cat(self.p_poses, dim=0)
762
+ pos_emb = self.positional_encoding(g.shape[0], g.shape[2], g.shape[3])
763
+ self.g_pos = rearrange(pos_emb, 'b c h w -> (h w) b c')
764
+
765
+ device = pools.device
766
+ self.p_poses = self.p_poses.to(device)
767
+ self.g_pos = self.g_pos.to(device)
768
+
769
+ # attention between glb (q) & multisensory concated-locs (k,v)
770
+ g_hw_b_c = rearrange(g, 'b c h w -> (h w) b c')
771
+
772
+ g_hw_b_c = g_hw_b_c + self.dropout1(self.attention[0](g_hw_b_c + self.g_pos, pools + self.p_poses, pools)[0])
773
+ g_hw_b_c = self.norm1(g_hw_b_c)
774
+ g_hw_b_c = g_hw_b_c + self.dropout2(self.linear2(self.dropout(self.activation(self.linear1(g_hw_b_c)).clone())))
775
+ g_hw_b_c = self.norm2(g_hw_b_c)
776
+
777
+ # attention between origin locs (q) & freashed glb (k,v)
778
+ l_hw_b_c = rearrange(l, "b c h w -> (h w) b c")
779
+ _g_hw_b_c = rearrange(g_hw_b_c, '(h w) b c -> h w b c', h=h, w=w)
780
+ _g_hw_b_c = rearrange(_g_hw_b_c, "(ng h) (nw w) b c -> (h w) (ng nw b) c", ng=2, nw=2)
781
+ outputs_re = []
782
+ for i, (_l, _g) in enumerate(zip(l_hw_b_c.chunk(4, dim=1), _g_hw_b_c.chunk(4, dim=1))):
783
+ outputs_re.append(self.attention[i + 1](_l, _g, _g)[0]) # (h w) 1 c
784
+ outputs_re = torch.cat(outputs_re, 1) # (h w) 4 c
785
+
786
+ l_hw_b_c = l_hw_b_c + self.dropout1(outputs_re)
787
+ l_hw_b_c = self.norm1(l_hw_b_c)
788
+ l_hw_b_c = l_hw_b_c + self.dropout2(self.linear4(self.dropout(self.activation(self.linear3(l_hw_b_c)).clone())))
789
+ l_hw_b_c = self.norm2(l_hw_b_c)
790
+
791
+ l = torch.cat((l_hw_b_c, g_hw_b_c), 1) # hw,b(5),c
792
+ return rearrange(l, "(h w) b c -> b c h w", h=h, w=w) ## (5,c,h*w)
793
+
794
+
795
+ class MCRM(nn.Module):
796
+ def __init__(self, d_model, num_heads, pool_ratios=[4, 8, 16], h=None):
797
+ super(MCRM, self).__init__()
798
+ self.attention = nn.ModuleList([
799
+ nn.MultiheadAttention(d_model, num_heads, dropout=0.1),
800
+ nn.MultiheadAttention(d_model, num_heads, dropout=0.1),
801
+ nn.MultiheadAttention(d_model, num_heads, dropout=0.1),
802
+ nn.MultiheadAttention(d_model, num_heads, dropout=0.1)
803
+ ])
804
+ self.linear3 = nn.Linear(d_model, d_model * 2)
805
+ self.linear4 = nn.Linear(d_model * 2, d_model)
806
+ self.norm1 = nn.LayerNorm(d_model)
807
+ self.norm2 = nn.LayerNorm(d_model)
808
+ self.dropout = nn.Dropout(0.1)
809
+ self.dropout1 = nn.Dropout(0.1)
810
+ self.dropout2 = nn.Dropout(0.1)
811
+ self.sigmoid = nn.Sigmoid()
812
+ self.activation = get_activation_fn('gelu')
813
+ self.sal_conv = nn.Conv2d(d_model, 1, 1)
814
+ self.pool_ratios = pool_ratios
815
+
816
+ def forward(self, x):
817
+ device = x.device
818
+ b, c, h, w = x.size()
819
+ loc, glb = x.split([4, 1], dim=0) # 4,c,h,w; 1,c,h,w
820
+
821
+ patched_glb = rearrange(glb, 'b c (hg h) (wg w) -> (hg wg b) c h w', hg=2, wg=2)
822
+
823
+ token_attention_map = self.sigmoid(self.sal_conv(glb))
824
+ token_attention_map = F.interpolate(token_attention_map, size=patches2image(loc).shape[-2:], mode='nearest')
825
+ loc = loc * rearrange(token_attention_map, 'b c (hg h) (wg w) -> (hg wg b) c h w', hg=2, wg=2)
826
+
827
+ pools = []
828
+ for pool_ratio in self.pool_ratios:
829
+ tgt_hw = (round(h / pool_ratio), round(w / pool_ratio))
830
+ pool = F.adaptive_avg_pool2d(patched_glb, tgt_hw)
831
+ pools.append(rearrange(pool, 'nl c h w -> nl c (h w)')) # nl(4),c,hw
832
+
833
+ pools = rearrange(torch.cat(pools, 2), "nl c nphw -> nl nphw 1 c")
834
+ loc_ = rearrange(loc, 'nl c h w -> nl (h w) 1 c')
835
+
836
+ outputs = []
837
+ for i, q in enumerate(loc_.unbind(dim=0)): # traverse all local patches
838
+ v = pools[i]
839
+ k = v
840
+ outputs.append(self.attention[i](q, k, v)[0])
841
+
842
+ outputs = torch.cat(outputs, 1)
843
+ src = loc.view(4, c, -1).permute(2, 0, 1) + self.dropout1(outputs)
844
+ src = self.norm1(src)
845
+ src = src + self.dropout2(self.linear4(self.dropout(self.activation(self.linear3(src)).clone())))
846
+ src = self.norm2(src)
847
+ src = src.permute(1, 2, 0).reshape(4, c, h, w) # freshed loc
848
+ glb = glb + F.interpolate(patches2image(src), size=glb.shape[-2:], mode='nearest') # freshed glb
849
+
850
+ return torch.cat((src, glb), 0), token_attention_map
851
+
852
+
853
+ class BEN_Base(nn.Module):
854
+ def __init__(self):
855
+ super().__init__()
856
+
857
+ self.backbone = SwinTransformer(embed_dim=128, depths=[2, 2, 18, 2], num_heads=[4, 8, 16, 32], window_size=12)
858
+ emb_dim = 128
859
+ self.sideout5 = nn.Sequential(nn.Conv2d(emb_dim, 1, kernel_size=3, padding=1))
860
+ self.sideout4 = nn.Sequential(nn.Conv2d(emb_dim, 1, kernel_size=3, padding=1))
861
+ self.sideout3 = nn.Sequential(nn.Conv2d(emb_dim, 1, kernel_size=3, padding=1))
862
+ self.sideout2 = nn.Sequential(nn.Conv2d(emb_dim, 1, kernel_size=3, padding=1))
863
+ self.sideout1 = nn.Sequential(nn.Conv2d(emb_dim, 1, kernel_size=3, padding=1))
864
+
865
+ self.output5 = make_cbr(1024, emb_dim)
866
+ self.output4 = make_cbr(512, emb_dim)
867
+ self.output3 = make_cbr(256, emb_dim)
868
+ self.output2 = make_cbr(128, emb_dim)
869
+ self.output1 = make_cbr(128, emb_dim)
870
+
871
+ self.multifieldcrossatt = MCLM(emb_dim, 1, [1, 4, 8])
872
+ self.conv1 = make_cbr(emb_dim, emb_dim)
873
+ self.conv2 = make_cbr(emb_dim, emb_dim)
874
+ self.conv3 = make_cbr(emb_dim, emb_dim)
875
+ self.conv4 = make_cbr(emb_dim, emb_dim)
876
+ self.dec_blk1 = MCRM(emb_dim, 1, [2, 4, 8])
877
+ self.dec_blk2 = MCRM(emb_dim, 1, [2, 4, 8])
878
+ self.dec_blk3 = MCRM(emb_dim, 1, [2, 4, 8])
879
+ self.dec_blk4 = MCRM(emb_dim, 1, [2, 4, 8])
880
+
881
+ self.insmask_head = nn.Sequential(
882
+ nn.Conv2d(emb_dim, 384, kernel_size=3, padding=1),
883
+ nn.InstanceNorm2d(384),
884
+ nn.GELU(),
885
+ nn.Conv2d(384, 384, kernel_size=3, padding=1),
886
+ nn.InstanceNorm2d(384),
887
+ nn.GELU(),
888
+ nn.Conv2d(384, emb_dim, kernel_size=3, padding=1)
889
+ )
890
+
891
+ self.shallow = nn.Sequential(nn.Conv2d(3, emb_dim, kernel_size=3, padding=1))
892
+ self.upsample1 = make_cbg(emb_dim, emb_dim)
893
+ self.upsample2 = make_cbg(emb_dim, emb_dim)
894
+ self.output = nn.Sequential(nn.Conv2d(emb_dim, 1, kernel_size=3, padding=1))
895
+
896
+ for m in self.modules():
897
+ if isinstance(m, nn.GELU) or isinstance(m, nn.Dropout):
898
+ m.inplace = True
899
+
900
+ @torch.inference_mode()
901
+ @torch.autocast(device_type="cuda", dtype=torch.float16)
902
+ def forward(self, x):
903
+ real_batch = x.size(0)
904
+
905
+ shallow_batch = self.shallow(x)
906
+ glb_batch = rescale_to(x, scale_factor=0.5, interpolation='bilinear')
907
+
908
+ final_input = None
909
+ for i in range(real_batch):
910
+ start = i * 4
911
+ end = (i + 1) * 4
912
+ loc_batch = image2patches(x[i, :, :, :].unsqueeze(dim=0))
913
+ input_ = torch.cat((loc_batch, glb_batch[i, :, :, :].unsqueeze(dim=0)), dim=0)
914
+
915
+ if final_input == None:
916
+ final_input = input_
917
+ else:
918
+ final_input = torch.cat((final_input, input_), dim=0)
919
+
920
+ features = self.backbone(final_input)
921
+ outputs = []
922
+
923
+ for i in range(real_batch):
924
+ start = i * 5
925
+ end = (i + 1) * 5
926
+
927
+ f4 = features[4][start:end, :, :, :] # shape: [5, C, H, W]
928
+ f3 = features[3][start:end, :, :, :]
929
+ f2 = features[2][start:end, :, :, :]
930
+ f1 = features[1][start:end, :, :, :]
931
+ f0 = features[0][start:end, :, :, :]
932
+ e5 = self.output5(f4)
933
+ e4 = self.output4(f3)
934
+ e3 = self.output3(f2)
935
+ e2 = self.output2(f1)
936
+ e1 = self.output1(f0)
937
+ loc_e5, glb_e5 = e5.split([4, 1], dim=0)
938
+ e5 = self.multifieldcrossatt(loc_e5, glb_e5) # (4,128,16,16)
939
+
940
+ e4, tokenattmap4 = self.dec_blk4(e4 + resize_as(e5, e4))
941
+ e4 = self.conv4(e4)
942
+ e3, tokenattmap3 = self.dec_blk3(e3 + resize_as(e4, e3))
943
+ e3 = self.conv3(e3)
944
+ e2, tokenattmap2 = self.dec_blk2(e2 + resize_as(e3, e2))
945
+ e2 = self.conv2(e2)
946
+ e1, tokenattmap1 = self.dec_blk1(e1 + resize_as(e2, e1))
947
+ e1 = self.conv1(e1)
948
+
949
+ loc_e1, glb_e1 = e1.split([4, 1], dim=0)
950
+
951
+ output1_cat = patches2image(loc_e1) # (1,128,256,256)
952
+
953
+ # add glb feat in
954
+ output1_cat = output1_cat + resize_as(glb_e1, output1_cat)
955
+ # merge
956
+ final_output = self.insmask_head(output1_cat) # (1,128,256,256)
957
+ # shallow feature merge
958
+ shallow = shallow_batch[i, :, :, :].unsqueeze(dim=0)
959
+ final_output = final_output + resize_as(shallow, final_output)
960
+ final_output = self.upsample1(rescale_to(final_output))
961
+ final_output = rescale_to(final_output + resize_as(shallow, final_output))
962
+ final_output = self.upsample2(final_output)
963
+ final_output = self.output(final_output)
964
+ mask = final_output.sigmoid()
965
+ outputs.append(mask)
966
+
967
+ return torch.cat(outputs, dim=0)
968
+
969
+ def loadcheckpoints(self, model_path):
970
+ model_dict = torch.load(model_path, map_location="cpu", weights_only=True)
971
+ self.load_state_dict(model_dict['model_state_dict'], strict=True)
972
+ del model_path
973
+
974
+ def inference(self, image, refine_foreground=False):
975
+
976
+ set_random_seed(9)
977
+ # image = ImageOps.exif_transpose(image)
978
+ if isinstance(image, Image.Image):
979
+ image, h, w, original_image = rgb_loader_refiner(image)
980
+ if torch.cuda.is_available():
981
+
982
+ img_tensor = img_transform(image).unsqueeze(0).to(next(self.parameters()).device)
983
+ else:
984
+ img_tensor = img_transform32(image).unsqueeze(0).to(next(self.parameters()).device)
985
+
986
+ with torch.no_grad():
987
+ res = self.forward(img_tensor)
988
+
989
+ # Show Results
990
+ if refine_foreground == True:
991
+
992
+ pred_pil = transforms.ToPILImage()(res.squeeze())
993
+ image_masked = refine_foreground_process(original_image, pred_pil)
994
+ mask = pred_pil.resize(original_image.size)
995
+ image_masked.putalpha(mask)
996
+ return mask, image_masked
997
+
998
+ else:
999
+ alpha = postprocess_image(res, im_size=[w, h])
1000
+ pred_pil = transforms.ToPILImage()(alpha)
1001
+ mask = pred_pil.resize(original_image.size)
1002
+ original_image.putalpha(mask)
1003
+ # mask = Image.fromarray(alpha)
1004
+
1005
+ return mask, original_image
1006
+
1007
+ def segment_video(self, video_path, output_path="./", fps=0, refine_foreground=False, batch=1,
1008
+ print_frames_processed=True, webm=False, rgb_value=(0, 255, 0)):
1009
+
1010
+ """
1011
+ Segments the given video to extract the foreground (with alpha) from each frame
1012
+ and saves the result as either a WebM video (with alpha channel) or MP4 (with a
1013
+ color background).
1014
+ Args:
1015
+ video_path (str):
1016
+ Path to the input video file.
1017
+ output_path (str, optional):
1018
+ Directory (or full path) where the output video and/or files will be saved.
1019
+ Defaults to "./".
1020
+ fps (int, optional):
1021
+ The frames per second (FPS) to use for the output video. If 0 (default), the
1022
+ original FPS of the input video is used. Otherwise, overrides it.
1023
+ refine_foreground (bool, optional):
1024
+ Whether to run an additional “refine foreground” process on each frame.
1025
+ Defaults to False.
1026
+ batch (int, optional):
1027
+ Number of frames to process at once (inference batch size). Large batch sizes
1028
+ may require more GPU memory. Defaults to 1.
1029
+ print_frames_processed (bool, optional):
1030
+ If True (default), prints progress (how many frames have been processed) to
1031
+ the console.
1032
+ webm (bool, optional):
1033
+ If True (default), exports a WebM video with alpha channel (VP9 / yuva420p).
1034
+ If False, exports an MP4 video composited over a solid color background.
1035
+ rgb_value (tuple, optional):
1036
+ The RGB background color (e.g., green screen) used to composite frames when
1037
+ saving to MP4. Defaults to (0, 255, 0).
1038
+ Returns:
1039
+ None. Writes the output video(s) to disk in the specified format.
1040
+ """
1041
+
1042
+ cap = cv2.VideoCapture(video_path)
1043
+ if not cap.isOpened():
1044
+ raise IOError(f"Cannot open video: {video_path}")
1045
+
1046
+ original_fps = cap.get(cv2.CAP_PROP_FPS)
1047
+ original_fps = 30 if original_fps == 0 else original_fps
1048
+ fps = original_fps if fps == 0 else fps
1049
+
1050
+ ret, first_frame = cap.read()
1051
+ if not ret:
1052
+ raise ValueError("No frames found in the video.")
1053
+ height, width = first_frame.shape[:2]
1054
+ cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
1055
+
1056
+ foregrounds = []
1057
+ frame_idx = 0
1058
+ processed_count = 0
1059
+ batch_frames = []
1060
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
1061
+
1062
+ while True:
1063
+ ret, frame = cap.read()
1064
+ if not ret:
1065
+ if batch_frames:
1066
+ batch_results = self.inference(batch_frames, refine_foreground)
1067
+ if isinstance(batch_results, Image.Image):
1068
+ foregrounds.append(batch_results)
1069
+ else:
1070
+ foregrounds.extend(batch_results)
1071
+ if print_frames_processed:
1072
+ print(f"Processed frames {frame_idx - len(batch_frames) + 1} to {frame_idx} of {total_frames}")
1073
+ break
1074
+
1075
+ # Process every frame instead of using intervals
1076
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
1077
+ pil_frame = Image.fromarray(frame_rgb)
1078
+ batch_frames.append(pil_frame)
1079
+
1080
+ if len(batch_frames) == batch:
1081
+ batch_results = self.inference(batch_frames, refine_foreground)
1082
+ if isinstance(batch_results, Image.Image):
1083
+ foregrounds.append(batch_results)
1084
+ else:
1085
+ foregrounds.extend(batch_results)
1086
+ if print_frames_processed:
1087
+ print(f"Processed frames {frame_idx - batch + 1} to {frame_idx} of {total_frames}")
1088
+ batch_frames = []
1089
+ processed_count += batch
1090
+
1091
+ frame_idx += 1
1092
+
1093
+ if webm:
1094
+ alpha_webm_path = os.path.join(output_path, "foreground.webm")
1095
+ pil_images_to_webm_alpha(foregrounds, alpha_webm_path, fps=original_fps)
1096
+
1097
+ else:
1098
+ cap.release()
1099
+ fg_output = os.path.join(output_path, 'foreground.mp4')
1100
+
1101
+ pil_images_to_mp4(foregrounds, fg_output, fps=original_fps, rgb_value=rgb_value)
1102
+ cv2.destroyAllWindows()
1103
+
1104
+ try:
1105
+ fg_audio_output = os.path.join(output_path, 'foreground_output_with_audio.mp4')
1106
+ add_audio_to_video(fg_output, video_path, fg_audio_output)
1107
+ except Exception as e:
1108
+ print("No audio found in the original video")
1109
+ print(e)
1110
+
1111
+
1112
+ def rgb_loader_refiner(original_image):
1113
+ h, w = original_image.size
1114
+
1115
+ image = original_image
1116
+ # Convert to RGB if necessary
1117
+ if image.mode != 'RGB':
1118
+ image = image.convert('RGB')
1119
+
1120
+ # Resize the image
1121
+ image = image.resize((1024, 1024), resample=Image.LANCZOS)
1122
+
1123
+ return image.convert('RGB'), h, w, original_image
1124
+
1125
+
1126
+ # Define the image transformation
1127
+ img_transform = transforms.Compose([
1128
+ transforms.ToTensor(),
1129
+ transforms.ConvertImageDtype(torch.float16),
1130
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
1131
+ ])
1132
+
1133
+ img_transform32 = transforms.Compose([
1134
+ transforms.ToTensor(),
1135
+ transforms.ConvertImageDtype(torch.float32),
1136
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
1137
+ ])
1138
+
1139
+
1140
+ def pil_images_to_mp4(images, output_path, fps=24, rgb_value=(0, 255, 0)):
1141
+ """
1142
+ Converts an array of PIL images to an MP4 video.
1143
+
1144
+ Args:
1145
+ images: List of PIL images
1146
+ output_path: Path to save the MP4 file
1147
+ fps: Frames per second (default: 24)
1148
+ rgb_value: Background RGB color tuple (default: green (0, 255, 0))
1149
+ """
1150
+ if not images:
1151
+ raise ValueError("No images provided to convert to MP4.")
1152
+
1153
+ width, height = images[0].size
1154
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
1155
+ video_writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
1156
+
1157
+ for image in images:
1158
+ # If image has alpha channel, composite onto the specified background color
1159
+ if image.mode == 'RGBA':
1160
+ # Create background image with specified RGB color
1161
+ background = Image.new('RGB', image.size, rgb_value)
1162
+ background = background.convert('RGBA')
1163
+ # Composite the image onto the background
1164
+ image = Image.alpha_composite(background, image)
1165
+ image = image.convert('RGB')
1166
+ else:
1167
+ # Ensure RGB format for non-alpha images
1168
+ image = image.convert('RGB')
1169
+
1170
+ # Convert to OpenCV format and write
1171
+ open_cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
1172
+ video_writer.write(open_cv_image)
1173
+
1174
+ video_writer.release()
1175
+
1176
+
1177
+ def pil_images_to_webm_alpha(images, output_path, fps=30):
1178
+ """
1179
+ Converts a list of PIL RGBA images to a VP9 .webm video with alpha channel.
1180
+ NOTE: Not all players will display alpha in WebM.
1181
+ Browsers like Chrome/Firefox typically do support VP9 alpha.
1182
+ """
1183
+ if not images:
1184
+ raise ValueError("No images provided for WebM with alpha.")
1185
+
1186
+ # Ensure output directory exists
1187
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
1188
+
1189
+ with tempfile.TemporaryDirectory() as tmpdir:
1190
+ # Save frames as PNG (with alpha)
1191
+ for idx, img in enumerate(images):
1192
+ if img.mode != "RGBA":
1193
+ img = img.convert("RGBA")
1194
+ out_path = os.path.join(tmpdir, f"{idx:06d}.png")
1195
+ img.save(out_path, "PNG")
1196
+
1197
+ # Construct ffmpeg command
1198
+ # -c:v libvpx-vp9 => VP9 encoder
1199
+ # -pix_fmt yuva420p => alpha-enabled pixel format
1200
+ # -auto-alt-ref 0 => helps preserve alpha frames (libvpx quirk)
1201
+ ffmpeg_cmd = [
1202
+ "ffmpeg", "-y",
1203
+ "-framerate", str(fps),
1204
+ "-i", os.path.join(tmpdir, "%06d.png"),
1205
+ "-c:v", "libvpx-vp9",
1206
+ "-pix_fmt", "yuva420p",
1207
+ "-auto-alt-ref", "0",
1208
+ output_path
1209
+ ]
1210
+
1211
+ subprocess.run(ffmpeg_cmd, check=True)
1212
+
1213
+ print(f"WebM with alpha saved to {output_path}")
1214
+
1215
+
1216
+ def add_audio_to_video(video_without_audio_path, original_video_path, output_path):
1217
+ """
1218
+ Check if the original video has an audio stream. If yes, add it. If not, skip.
1219
+ """
1220
+ # 1) Probe original video for audio streams
1221
+ probe_command = [
1222
+ 'ffprobe', '-v', 'error',
1223
+ '-select_streams', 'a:0',
1224
+ '-show_entries', 'stream=index',
1225
+ '-of', 'csv=p=0',
1226
+ original_video_path
1227
+ ]
1228
+ result = subprocess.run(probe_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
1229
+
1230
+ # result.stdout is empty if no audio stream found
1231
+ if not result.stdout.strip():
1232
+ print("No audio track found in original video, skipping audio addition.")
1233
+ return
1234
+
1235
+ print("Audio track detected; proceeding to mux audio.")
1236
+ # 2) If audio found, run ffmpeg to add it
1237
+ command = [
1238
+ 'ffmpeg', '-y',
1239
+ '-i', video_without_audio_path,
1240
+ '-i', original_video_path,
1241
+ '-c', 'copy',
1242
+ '-map', '0:v:0',
1243
+ '-map', '1:a:0', # we know there's an audio track now
1244
+ output_path
1245
+ ]
1246
+ subprocess.run(command, check=True)
1247
+ print(f"Audio added successfully => {output_path}")
1248
+
1249
+
1250
+ ### Thanks to the source: https://huggingface.co/ZhengPeng7/BiRefNet/blob/main/handler.py
1251
+ def refine_foreground_process(image, mask, r=90):
1252
+ if mask.size != image.size:
1253
+ mask = mask.resize(image.size)
1254
+ image = np.array(image) / 255.0
1255
+ mask = np.array(mask) / 255.0
1256
+ estimated_foreground = FB_blur_fusion_foreground_estimator_2(image, mask, r=r)
1257
+ image_masked = Image.fromarray((estimated_foreground * 255.0).astype(np.uint8))
1258
+ return image_masked
1259
+
1260
+
1261
+ def FB_blur_fusion_foreground_estimator_2(image, alpha, r=90):
1262
+ # Thanks to the source: https://github.com/Photoroom/fast-foreground-estimation
1263
+ alpha = alpha[:, :, None]
1264
+ F, blur_B = FB_blur_fusion_foreground_estimator(image, image, image, alpha, r)
1265
+ return FB_blur_fusion_foreground_estimator(image, F, blur_B, alpha, r=6)[0]
1266
+
1267
+
1268
+ def FB_blur_fusion_foreground_estimator(image, F, B, alpha, r=90):
1269
+ if isinstance(image, Image.Image):
1270
+ image = np.array(image) / 255.0
1271
+ blurred_alpha = cv2.blur(alpha, (r, r))[:, :, None]
1272
+
1273
+ blurred_FA = cv2.blur(F * alpha, (r, r))
1274
+ blurred_F = blurred_FA / (blurred_alpha + 1e-5)
1275
+
1276
+ blurred_B1A = cv2.blur(B * (1 - alpha), (r, r))
1277
+ blurred_B = blurred_B1A / ((1 - blurred_alpha) + 1e-5)
1278
+ F = blurred_F + alpha * \
1279
+ (image - alpha * blurred_F - (1 - alpha) * blurred_B)
1280
+ F = np.clip(F, 0, 1)
1281
+ return F, blurred_B
1282
+
1283
+
1284
+ def postprocess_image(result: torch.Tensor, im_size: list) -> np.ndarray:
1285
+ result = torch.squeeze(F.interpolate(result, size=im_size, mode='bilinear'), 0)
1286
+ ma = torch.max(result)
1287
+ mi = torch.min(result)
1288
+ result = (result - mi) / (ma - mi)
1289
+ im_array = (result * 255).permute(1, 2, 0).cpu().data.numpy().astype(np.uint8)
1290
+ im_array = np.squeeze(im_array)
1291
+ return im_array
1292
+
1293
+
1294
+ def rgb_loader_refiner(original_image):
1295
+ h, w = original_image.size
1296
+ # # Apply EXIF orientation
1297
+
1298
+ image = ImageOps.exif_transpose(original_image)
1299
+
1300
+ if original_image.mode != 'RGB':
1301
+ original_image = original_image.convert('RGB')
1302
+
1303
+ image = original_image
1304
+ # Convert to RGB if necessary
1305
+
1306
+ # Resize the image
1307
+ image = image.resize((1024, 1024), resample=Image.LANCZOS)
1308
+
1309
+ return image, h, w, original_image
1310
+
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/bitsandbytes_NF4/__init__.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #credit to comfyanonymous for this module
2
+ #from https://github.com/comfyanonymous/ComfyUI_bitsandbytes_NF4
3
+ import comfy.ops
4
+ import torch
5
+ import folder_paths
6
+ from ...libs.utils import install_package
7
+
8
+ try:
9
+ from bitsandbytes.nn.modules import Params4bit, QuantState
10
+ except ImportError:
11
+ Params4bit = torch.nn.Parameter
12
+ raise ImportError("Please install bitsandbytes>=0.43.3")
13
+
14
+ def functional_linear_4bits(x, weight, bias):
15
+ try:
16
+ install_package("bitsandbytes", "0.43.3", True, "0.43.3")
17
+ import bitsandbytes as bnb
18
+ except ImportError:
19
+ raise ImportError("Please install bitsandbytes>=0.43.3")
20
+
21
+ out = bnb.matmul_4bit(x, weight.t(), bias=bias, quant_state=weight.quant_state)
22
+ out = out.to(x)
23
+ return out
24
+
25
+
26
+ def copy_quant_state(state, device: torch.device = None):
27
+ if state is None:
28
+ return None
29
+
30
+ device = device or state.absmax.device
31
+
32
+ state2 = (
33
+ QuantState(
34
+ absmax=state.state2.absmax.to(device),
35
+ shape=state.state2.shape,
36
+ code=state.state2.code.to(device),
37
+ blocksize=state.state2.blocksize,
38
+ quant_type=state.state2.quant_type,
39
+ dtype=state.state2.dtype,
40
+ )
41
+ if state.nested
42
+ else None
43
+ )
44
+
45
+ return QuantState(
46
+ absmax=state.absmax.to(device),
47
+ shape=state.shape,
48
+ code=state.code.to(device),
49
+ blocksize=state.blocksize,
50
+ quant_type=state.quant_type,
51
+ dtype=state.dtype,
52
+ offset=state.offset.to(device) if state.nested else None,
53
+ state2=state2,
54
+ )
55
+
56
+
57
+ class ForgeParams4bit(Params4bit):
58
+
59
+ def to(self, *args, **kwargs):
60
+ device, dtype, non_blocking, convert_to_format = torch._C._nn._parse_to(*args, **kwargs)
61
+ if device is not None and device.type == "cuda" and not self.bnb_quantized:
62
+ return self._quantize(device)
63
+ else:
64
+ n = ForgeParams4bit(
65
+ torch.nn.Parameter.to(self, device=device, dtype=dtype, non_blocking=non_blocking),
66
+ requires_grad=self.requires_grad,
67
+ quant_state=copy_quant_state(self.quant_state, device),
68
+ blocksize=self.blocksize,
69
+ compress_statistics=self.compress_statistics,
70
+ quant_type=self.quant_type,
71
+ quant_storage=self.quant_storage,
72
+ bnb_quantized=self.bnb_quantized,
73
+ module=self.module
74
+ )
75
+ self.module.quant_state = n.quant_state
76
+ self.data = n.data
77
+ self.quant_state = n.quant_state
78
+ return n
79
+
80
+ class ForgeLoader4Bit(torch.nn.Module):
81
+ def __init__(self, *, device, dtype, quant_type, **kwargs):
82
+ super().__init__()
83
+ self.dummy = torch.nn.Parameter(torch.empty(1, device=device, dtype=dtype))
84
+ self.weight = None
85
+ self.quant_state = None
86
+ self.bias = None
87
+ self.quant_type = quant_type
88
+
89
+ def _save_to_state_dict(self, destination, prefix, keep_vars):
90
+ super()._save_to_state_dict(destination, prefix, keep_vars)
91
+ quant_state = getattr(self.weight, "quant_state", None)
92
+ if quant_state is not None:
93
+ for k, v in quant_state.as_dict(packed=True).items():
94
+ destination[prefix + "weight." + k] = v if keep_vars else v.detach()
95
+ return
96
+
97
+ def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs):
98
+ quant_state_keys = {k[len(prefix + "weight."):] for k in state_dict.keys() if k.startswith(prefix + "weight.")}
99
+
100
+ if any('bitsandbytes' in k for k in quant_state_keys):
101
+ quant_state_dict = {k: state_dict[prefix + "weight." + k] for k in quant_state_keys}
102
+
103
+ self.weight = ForgeParams4bit().from_prequantized(
104
+ data=state_dict[prefix + 'weight'],
105
+ quantized_stats=quant_state_dict,
106
+ requires_grad=False,
107
+ device=self.dummy.device,
108
+ module=self
109
+ )
110
+ self.quant_state = self.weight.quant_state
111
+
112
+ if prefix + 'bias' in state_dict:
113
+ self.bias = torch.nn.Parameter(state_dict[prefix + 'bias'].to(self.dummy))
114
+
115
+ del self.dummy
116
+ elif hasattr(self, 'dummy'):
117
+ if prefix + 'weight' in state_dict:
118
+ self.weight = ForgeParams4bit(
119
+ state_dict[prefix + 'weight'].to(self.dummy),
120
+ requires_grad=False,
121
+ compress_statistics=True,
122
+ quant_type=self.quant_type,
123
+ quant_storage=torch.uint8,
124
+ module=self,
125
+ )
126
+ self.quant_state = self.weight.quant_state
127
+
128
+ if prefix + 'bias' in state_dict:
129
+ self.bias = torch.nn.Parameter(state_dict[prefix + 'bias'].to(self.dummy))
130
+
131
+ del self.dummy
132
+ else:
133
+ super()._load_from_state_dict(state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs)
134
+
135
+ current_device = None
136
+ current_dtype = None
137
+ current_manual_cast_enabled = False
138
+ current_bnb_dtype = None
139
+
140
+ class OPS(comfy.ops.manual_cast):
141
+ class Linear(ForgeLoader4Bit):
142
+ def __init__(self, *args, device=None, dtype=None, **kwargs):
143
+ super().__init__(device=device, dtype=dtype, quant_type=current_bnb_dtype)
144
+ self.parameters_manual_cast = current_manual_cast_enabled
145
+
146
+ def forward(self, x):
147
+ self.weight.quant_state = self.quant_state
148
+
149
+ if self.bias is not None and self.bias.dtype != x.dtype:
150
+ # Maybe this can also be set to all non-bnb ops since the cost is very low.
151
+ # And it only invokes one time, and most linear does not have bias
152
+ self.bias.data = self.bias.data.to(x.dtype)
153
+
154
+ if not self.parameters_manual_cast:
155
+ return functional_linear_4bits(x, self.weight, self.bias)
156
+ elif not self.weight.bnb_quantized:
157
+ assert x.device.type == 'cuda', 'BNB Must Use CUDA as Computation Device!'
158
+ layer_original_device = self.weight.device
159
+ self.weight = self.weight._quantize(x.device)
160
+ bias = self.bias.to(x.device) if self.bias is not None else None
161
+ out = functional_linear_4bits(x, self.weight, bias)
162
+ self.weight = self.weight.to(layer_original_device)
163
+ return out
164
+ else:
165
+ weight, bias, signal = weights_manual_cast(self, x, skip_weight_dtype=True, skip_bias_dtype=True)
166
+ with main_stream_worker(weight, bias, signal):
167
+ return functional_linear_4bits(x, weight, bias)
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/briaai/__init__.py ADDED
File without changes
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/briaai/rembg.py ADDED
@@ -0,0 +1,475 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ from torchvision.transforms.functional import normalize
5
+ import numpy as np
6
+
7
+
8
+ class REBNCONV(nn.Module):
9
+ def __init__(self,in_ch=3,out_ch=3,dirate=1,stride=1):
10
+ super(REBNCONV,self).__init__()
11
+
12
+ self.conv_s1 = nn.Conv2d(in_ch,out_ch,3,padding=1*dirate,dilation=1*dirate,stride=stride)
13
+ self.bn_s1 = nn.BatchNorm2d(out_ch)
14
+ self.relu_s1 = nn.ReLU(inplace=True)
15
+
16
+ def forward(self,x):
17
+
18
+ hx = x
19
+ xout = self.relu_s1(self.bn_s1(self.conv_s1(hx)))
20
+
21
+ return xout
22
+
23
+ ## upsample tensor 'src' to have the same spatial size with tensor 'tar'
24
+ def _upsample_like(src,tar):
25
+
26
+ src = F.interpolate(src,size=tar.shape[2:],mode='bilinear')
27
+
28
+ return src
29
+
30
+
31
+ ### RSU-7 ###
32
+ class RSU7(nn.Module):
33
+
34
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3, img_size=512):
35
+ super(RSU7,self).__init__()
36
+
37
+ self.in_ch = in_ch
38
+ self.mid_ch = mid_ch
39
+ self.out_ch = out_ch
40
+
41
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1) ## 1 -> 1/2
42
+
43
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
44
+ self.pool1 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
45
+
46
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=1)
47
+ self.pool2 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
48
+
49
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=1)
50
+ self.pool3 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
51
+
52
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=1)
53
+ self.pool4 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
54
+
55
+ self.rebnconv5 = REBNCONV(mid_ch,mid_ch,dirate=1)
56
+ self.pool5 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
57
+
58
+ self.rebnconv6 = REBNCONV(mid_ch,mid_ch,dirate=1)
59
+
60
+ self.rebnconv7 = REBNCONV(mid_ch,mid_ch,dirate=2)
61
+
62
+ self.rebnconv6d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
63
+ self.rebnconv5d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
64
+ self.rebnconv4d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
65
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
66
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
67
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
68
+
69
+ def forward(self,x):
70
+ b, c, h, w = x.shape
71
+
72
+ hx = x
73
+ hxin = self.rebnconvin(hx)
74
+
75
+ hx1 = self.rebnconv1(hxin)
76
+ hx = self.pool1(hx1)
77
+
78
+ hx2 = self.rebnconv2(hx)
79
+ hx = self.pool2(hx2)
80
+
81
+ hx3 = self.rebnconv3(hx)
82
+ hx = self.pool3(hx3)
83
+
84
+ hx4 = self.rebnconv4(hx)
85
+ hx = self.pool4(hx4)
86
+
87
+ hx5 = self.rebnconv5(hx)
88
+ hx = self.pool5(hx5)
89
+
90
+ hx6 = self.rebnconv6(hx)
91
+
92
+ hx7 = self.rebnconv7(hx6)
93
+
94
+ hx6d = self.rebnconv6d(torch.cat((hx7,hx6),1))
95
+ hx6dup = _upsample_like(hx6d,hx5)
96
+
97
+ hx5d = self.rebnconv5d(torch.cat((hx6dup,hx5),1))
98
+ hx5dup = _upsample_like(hx5d,hx4)
99
+
100
+ hx4d = self.rebnconv4d(torch.cat((hx5dup,hx4),1))
101
+ hx4dup = _upsample_like(hx4d,hx3)
102
+
103
+ hx3d = self.rebnconv3d(torch.cat((hx4dup,hx3),1))
104
+ hx3dup = _upsample_like(hx3d,hx2)
105
+
106
+ hx2d = self.rebnconv2d(torch.cat((hx3dup,hx2),1))
107
+ hx2dup = _upsample_like(hx2d,hx1)
108
+
109
+ hx1d = self.rebnconv1d(torch.cat((hx2dup,hx1),1))
110
+
111
+ return hx1d + hxin
112
+
113
+
114
+ ### RSU-6 ###
115
+ class RSU6(nn.Module):
116
+
117
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3):
118
+ super(RSU6,self).__init__()
119
+
120
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1)
121
+
122
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
123
+ self.pool1 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
124
+
125
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=1)
126
+ self.pool2 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
127
+
128
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=1)
129
+ self.pool3 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
130
+
131
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=1)
132
+ self.pool4 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
133
+
134
+ self.rebnconv5 = REBNCONV(mid_ch,mid_ch,dirate=1)
135
+
136
+ self.rebnconv6 = REBNCONV(mid_ch,mid_ch,dirate=2)
137
+
138
+ self.rebnconv5d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
139
+ self.rebnconv4d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
140
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
141
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
142
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
143
+
144
+ def forward(self,x):
145
+
146
+ hx = x
147
+
148
+ hxin = self.rebnconvin(hx)
149
+
150
+ hx1 = self.rebnconv1(hxin)
151
+ hx = self.pool1(hx1)
152
+
153
+ hx2 = self.rebnconv2(hx)
154
+ hx = self.pool2(hx2)
155
+
156
+ hx3 = self.rebnconv3(hx)
157
+ hx = self.pool3(hx3)
158
+
159
+ hx4 = self.rebnconv4(hx)
160
+ hx = self.pool4(hx4)
161
+
162
+ hx5 = self.rebnconv5(hx)
163
+
164
+ hx6 = self.rebnconv6(hx5)
165
+
166
+
167
+ hx5d = self.rebnconv5d(torch.cat((hx6,hx5),1))
168
+ hx5dup = _upsample_like(hx5d,hx4)
169
+
170
+ hx4d = self.rebnconv4d(torch.cat((hx5dup,hx4),1))
171
+ hx4dup = _upsample_like(hx4d,hx3)
172
+
173
+ hx3d = self.rebnconv3d(torch.cat((hx4dup,hx3),1))
174
+ hx3dup = _upsample_like(hx3d,hx2)
175
+
176
+ hx2d = self.rebnconv2d(torch.cat((hx3dup,hx2),1))
177
+ hx2dup = _upsample_like(hx2d,hx1)
178
+
179
+ hx1d = self.rebnconv1d(torch.cat((hx2dup,hx1),1))
180
+
181
+ return hx1d + hxin
182
+
183
+ ### RSU-5 ###
184
+ class RSU5(nn.Module):
185
+
186
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3):
187
+ super(RSU5,self).__init__()
188
+
189
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1)
190
+
191
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
192
+ self.pool1 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
193
+
194
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=1)
195
+ self.pool2 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
196
+
197
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=1)
198
+ self.pool3 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
199
+
200
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=1)
201
+
202
+ self.rebnconv5 = REBNCONV(mid_ch,mid_ch,dirate=2)
203
+
204
+ self.rebnconv4d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
205
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
206
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
207
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
208
+
209
+ def forward(self,x):
210
+
211
+ hx = x
212
+
213
+ hxin = self.rebnconvin(hx)
214
+
215
+ hx1 = self.rebnconv1(hxin)
216
+ hx = self.pool1(hx1)
217
+
218
+ hx2 = self.rebnconv2(hx)
219
+ hx = self.pool2(hx2)
220
+
221
+ hx3 = self.rebnconv3(hx)
222
+ hx = self.pool3(hx3)
223
+
224
+ hx4 = self.rebnconv4(hx)
225
+
226
+ hx5 = self.rebnconv5(hx4)
227
+
228
+ hx4d = self.rebnconv4d(torch.cat((hx5,hx4),1))
229
+ hx4dup = _upsample_like(hx4d,hx3)
230
+
231
+ hx3d = self.rebnconv3d(torch.cat((hx4dup,hx3),1))
232
+ hx3dup = _upsample_like(hx3d,hx2)
233
+
234
+ hx2d = self.rebnconv2d(torch.cat((hx3dup,hx2),1))
235
+ hx2dup = _upsample_like(hx2d,hx1)
236
+
237
+ hx1d = self.rebnconv1d(torch.cat((hx2dup,hx1),1))
238
+
239
+ return hx1d + hxin
240
+
241
+ ### RSU-4 ###
242
+ class RSU4(nn.Module):
243
+
244
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3):
245
+ super(RSU4,self).__init__()
246
+
247
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1)
248
+
249
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
250
+ self.pool1 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
251
+
252
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=1)
253
+ self.pool2 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
254
+
255
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=1)
256
+
257
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=2)
258
+
259
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
260
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
261
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
262
+
263
+ def forward(self,x):
264
+
265
+ hx = x
266
+
267
+ hxin = self.rebnconvin(hx)
268
+
269
+ hx1 = self.rebnconv1(hxin)
270
+ hx = self.pool1(hx1)
271
+
272
+ hx2 = self.rebnconv2(hx)
273
+ hx = self.pool2(hx2)
274
+
275
+ hx3 = self.rebnconv3(hx)
276
+
277
+ hx4 = self.rebnconv4(hx3)
278
+
279
+ hx3d = self.rebnconv3d(torch.cat((hx4,hx3),1))
280
+ hx3dup = _upsample_like(hx3d,hx2)
281
+
282
+ hx2d = self.rebnconv2d(torch.cat((hx3dup,hx2),1))
283
+ hx2dup = _upsample_like(hx2d,hx1)
284
+
285
+ hx1d = self.rebnconv1d(torch.cat((hx2dup,hx1),1))
286
+
287
+ return hx1d + hxin
288
+
289
+ ### RSU-4F ###
290
+ class RSU4F(nn.Module):
291
+
292
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3):
293
+ super(RSU4F,self).__init__()
294
+
295
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1)
296
+
297
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
298
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=2)
299
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=4)
300
+
301
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=8)
302
+
303
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=4)
304
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=2)
305
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
306
+
307
+ def forward(self,x):
308
+
309
+ hx = x
310
+
311
+ hxin = self.rebnconvin(hx)
312
+
313
+ hx1 = self.rebnconv1(hxin)
314
+ hx2 = self.rebnconv2(hx1)
315
+ hx3 = self.rebnconv3(hx2)
316
+
317
+ hx4 = self.rebnconv4(hx3)
318
+
319
+ hx3d = self.rebnconv3d(torch.cat((hx4,hx3),1))
320
+ hx2d = self.rebnconv2d(torch.cat((hx3d,hx2),1))
321
+ hx1d = self.rebnconv1d(torch.cat((hx2d,hx1),1))
322
+
323
+ return hx1d + hxin
324
+
325
+
326
+ class myrebnconv(nn.Module):
327
+ def __init__(self, in_ch=3,
328
+ out_ch=1,
329
+ kernel_size=3,
330
+ stride=1,
331
+ padding=1,
332
+ dilation=1,
333
+ groups=1):
334
+ super(myrebnconv,self).__init__()
335
+
336
+ self.conv = nn.Conv2d(in_ch,
337
+ out_ch,
338
+ kernel_size=kernel_size,
339
+ stride=stride,
340
+ padding=padding,
341
+ dilation=dilation,
342
+ groups=groups)
343
+ self.bn = nn.BatchNorm2d(out_ch)
344
+ self.rl = nn.ReLU(inplace=True)
345
+
346
+ def forward(self,x):
347
+ return self.rl(self.bn(self.conv(x)))
348
+
349
+ def preprocess_image(im, model_input_size: list) -> torch.Tensor:
350
+ # im = im.resize(model_input_size, Image.BILINEAR)
351
+ im_np = np.array(im)
352
+ im_tensor = torch.tensor(im_np, dtype=torch.float32).permute(2,0,1)
353
+ im_tensor = F.interpolate(torch.unsqueeze(im_tensor,0), size=model_input_size, mode='bilinear').type(torch.uint8)
354
+ image = torch.divide(im_tensor,255.0)
355
+ image = normalize(image,[0.5,0.5,0.5],[1.0,1.0,1.0])
356
+ return image
357
+
358
+ def postprocess_image(result: torch.Tensor, im_size: list)-> np.ndarray:
359
+ result = torch.squeeze(F.interpolate(result, size=im_size, mode='bilinear') ,0)
360
+ ma = torch.max(result)
361
+ mi = torch.min(result)
362
+ result = (result-mi)/(ma-mi)
363
+ im_array = (result*255).permute(1,2,0).cpu().data.numpy().astype(np.uint8)
364
+ im_array = np.squeeze(im_array)
365
+ return im_array
366
+
367
+ class BriaRMBG(nn.Module):
368
+
369
+ def __init__(self, config:dict={"in_ch":3,"out_ch":1}):
370
+ super(BriaRMBG,self).__init__()
371
+ in_ch = config["in_ch"]
372
+ out_ch = config["out_ch"]
373
+ self.conv_in = nn.Conv2d(in_ch,64,3,stride=2,padding=1)
374
+ self.pool_in = nn.MaxPool2d(2,stride=2,ceil_mode=True)
375
+
376
+ self.stage1 = RSU7(64,32,64)
377
+ self.pool12 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
378
+
379
+ self.stage2 = RSU6(64,32,128)
380
+ self.pool23 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
381
+
382
+ self.stage3 = RSU5(128,64,256)
383
+ self.pool34 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
384
+
385
+ self.stage4 = RSU4(256,128,512)
386
+ self.pool45 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
387
+
388
+ self.stage5 = RSU4F(512,256,512)
389
+ self.pool56 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
390
+
391
+ self.stage6 = RSU4F(512,256,512)
392
+
393
+ # decoder
394
+ self.stage5d = RSU4F(1024,256,512)
395
+ self.stage4d = RSU4(1024,128,256)
396
+ self.stage3d = RSU5(512,64,128)
397
+ self.stage2d = RSU6(256,32,64)
398
+ self.stage1d = RSU7(128,16,64)
399
+
400
+ self.side1 = nn.Conv2d(64,out_ch,3,padding=1)
401
+ self.side2 = nn.Conv2d(64,out_ch,3,padding=1)
402
+ self.side3 = nn.Conv2d(128,out_ch,3,padding=1)
403
+ self.side4 = nn.Conv2d(256,out_ch,3,padding=1)
404
+ self.side5 = nn.Conv2d(512,out_ch,3,padding=1)
405
+ self.side6 = nn.Conv2d(512,out_ch,3,padding=1)
406
+
407
+ # self.outconv = nn.Conv2d(6*out_ch,out_ch,1)
408
+
409
+ def forward(self,x):
410
+
411
+ hx = x
412
+
413
+ hxin = self.conv_in(hx)
414
+ #hx = self.pool_in(hxin)
415
+
416
+ #stage 1
417
+ hx1 = self.stage1(hxin)
418
+ hx = self.pool12(hx1)
419
+
420
+ #stage 2
421
+ hx2 = self.stage2(hx)
422
+ hx = self.pool23(hx2)
423
+
424
+ #stage 3
425
+ hx3 = self.stage3(hx)
426
+ hx = self.pool34(hx3)
427
+
428
+ #stage 4
429
+ hx4 = self.stage4(hx)
430
+ hx = self.pool45(hx4)
431
+
432
+ #stage 5
433
+ hx5 = self.stage5(hx)
434
+ hx = self.pool56(hx5)
435
+
436
+ #stage 6
437
+ hx6 = self.stage6(hx)
438
+ hx6up = _upsample_like(hx6,hx5)
439
+
440
+ #-------------------- decoder --------------------
441
+ hx5d = self.stage5d(torch.cat((hx6up,hx5),1))
442
+ hx5dup = _upsample_like(hx5d,hx4)
443
+
444
+ hx4d = self.stage4d(torch.cat((hx5dup,hx4),1))
445
+ hx4dup = _upsample_like(hx4d,hx3)
446
+
447
+ hx3d = self.stage3d(torch.cat((hx4dup,hx3),1))
448
+ hx3dup = _upsample_like(hx3d,hx2)
449
+
450
+ hx2d = self.stage2d(torch.cat((hx3dup,hx2),1))
451
+ hx2dup = _upsample_like(hx2d,hx1)
452
+
453
+ hx1d = self.stage1d(torch.cat((hx2dup,hx1),1))
454
+
455
+
456
+ #side output
457
+ d1 = self.side1(hx1d)
458
+ d1 = _upsample_like(d1,x)
459
+
460
+ d2 = self.side2(hx2d)
461
+ d2 = _upsample_like(d2,x)
462
+
463
+ d3 = self.side3(hx3d)
464
+ d3 = _upsample_like(d3,x)
465
+
466
+ d4 = self.side4(hx4d)
467
+ d4 = _upsample_like(d4,x)
468
+
469
+ d5 = self.side5(hx5d)
470
+ d5 = _upsample_like(d5,x)
471
+
472
+ d6 = self.side6(hx6)
473
+ d6 = _upsample_like(d6,x)
474
+
475
+ return [F.sigmoid(d1), F.sigmoid(d2), F.sigmoid(d3), F.sigmoid(d4), F.sigmoid(d5), F.sigmoid(d6)],[hx1d,hx2d,hx3d,hx4d,hx5d,hx6]
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__init__.py ADDED
@@ -0,0 +1,822 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #credit to nullquant for this module
2
+ #from https://github.com/nullquant/ComfyUI-BrushNet
3
+
4
+ import os
5
+ import types
6
+
7
+ import torch
8
+ try:
9
+ from accelerate import init_empty_weights, load_checkpoint_and_dispatch
10
+ except:
11
+ init_empty_weights, load_checkpoint_and_dispatch = None, None
12
+
13
+ import comfy
14
+
15
+ try:
16
+ from .model import BrushNetModel, PowerPaintModel
17
+ from .model_patch import add_model_patch_option, patch_model_function_wrapper
18
+ from .powerpaint_utils import TokenizerWrapper, add_tokens
19
+ except:
20
+ BrushNetModel, PowerPaintModel = None, None
21
+ add_model_patch_option, patch_model_function_wrapper = None, None
22
+ TokenizerWrapper, add_tokens = None, None
23
+
24
+ cwd_path = os.path.dirname(os.path.realpath(__file__))
25
+ brushnet_config_file = os.path.join(cwd_path, 'config', 'brushnet.json')
26
+ brushnet_xl_config_file = os.path.join(cwd_path, 'config', 'brushnet_xl.json')
27
+ powerpaint_config_file = os.path.join(cwd_path, 'config', 'powerpaint.json')
28
+
29
+ sd15_scaling_factor = 0.18215
30
+ sdxl_scaling_factor = 0.13025
31
+
32
+ ModelsToUnload = [comfy.sd1_clip.SD1ClipModel, comfy.ldm.models.autoencoder.AutoencoderKL]
33
+
34
+ class BrushNet:
35
+
36
+ # Check models compatibility
37
+ def check_compatibilty(self, model, brushnet):
38
+ is_SDXL = False
39
+ is_PP = False
40
+ if isinstance(model.model.model_config, comfy.supported_models.SD15):
41
+ print('Base model type: SD1.5')
42
+ is_SDXL = False
43
+ if brushnet["SDXL"]:
44
+ raise Exception("Base model is SD15, but BrushNet is SDXL type")
45
+ if brushnet["PP"]:
46
+ is_PP = True
47
+ elif isinstance(model.model.model_config, comfy.supported_models.SDXL):
48
+ print('Base model type: SDXL')
49
+ is_SDXL = True
50
+ if not brushnet["SDXL"]:
51
+ raise Exception("Base model is SDXL, but BrushNet is SD15 type")
52
+ else:
53
+ print('Base model type: ', type(model.model.model_config))
54
+ raise Exception("Unsupported model type: " + str(type(model.model.model_config)))
55
+
56
+ return (is_SDXL, is_PP)
57
+
58
+ def check_image_mask(self, image, mask, name):
59
+ if len(image.shape) < 4:
60
+ # image tensor shape should be [B, H, W, C], but batch somehow is missing
61
+ image = image[None, :, :, :]
62
+
63
+ if len(mask.shape) > 3:
64
+ # mask tensor shape should be [B, H, W] but we get [B, H, W, C], image may be?
65
+ # take first mask, red channel
66
+ mask = (mask[:, :, :, 0])[:, :, :]
67
+ elif len(mask.shape) < 3:
68
+ # mask tensor shape should be [B, H, W] but batch somehow is missing
69
+ mask = mask[None, :, :]
70
+
71
+ if image.shape[0] > mask.shape[0]:
72
+ print(name, "gets batch of images (%d) but only %d masks" % (image.shape[0], mask.shape[0]))
73
+ if mask.shape[0] == 1:
74
+ print(name, "will copy the mask to fill batch")
75
+ mask = torch.cat([mask] * image.shape[0], dim=0)
76
+ else:
77
+ print(name, "will add empty masks to fill batch")
78
+ empty_mask = torch.zeros([image.shape[0] - mask.shape[0], mask.shape[1], mask.shape[2]])
79
+ mask = torch.cat([mask, empty_mask], dim=0)
80
+ elif image.shape[0] < mask.shape[0]:
81
+ print(name, "gets batch of images (%d) but too many (%d) masks" % (image.shape[0], mask.shape[0]))
82
+ mask = mask[:image.shape[0], :, :]
83
+
84
+ return (image, mask)
85
+
86
+ # Prepare image and mask
87
+ def prepare_image(self, image, mask):
88
+
89
+ image, mask = self.check_image_mask(image, mask, 'BrushNet')
90
+
91
+ print("BrushNet image.shape =", image.shape, "mask.shape =", mask.shape)
92
+
93
+ if mask.shape[2] != image.shape[2] or mask.shape[1] != image.shape[1]:
94
+ raise Exception("Image and mask should be the same size")
95
+
96
+ # As a suggestion of inferno46n2 (https://github.com/nullquant/ComfyUI-BrushNet/issues/64)
97
+ mask = mask.round()
98
+
99
+ masked_image = image * (1.0 - mask[:, :, :, None])
100
+
101
+ return (masked_image, mask)
102
+
103
+ # Get origin of the mask
104
+ def cut_with_mask(self, mask, width, height):
105
+ iy, ix = (mask == 1).nonzero(as_tuple=True)
106
+
107
+ h0, w0 = mask.shape
108
+
109
+ if iy.numel() == 0:
110
+ x_c = w0 / 2.0
111
+ y_c = h0 / 2.0
112
+ else:
113
+ x_min = ix.min().item()
114
+ x_max = ix.max().item()
115
+ y_min = iy.min().item()
116
+ y_max = iy.max().item()
117
+
118
+ if x_max - x_min > width or y_max - y_min > height:
119
+ raise Exception("Mask is bigger than provided dimensions")
120
+
121
+ x_c = (x_min + x_max) / 2.0
122
+ y_c = (y_min + y_max) / 2.0
123
+
124
+ width2 = width / 2.0
125
+ height2 = height / 2.0
126
+
127
+ if w0 <= width:
128
+ x0 = 0
129
+ w = w0
130
+ else:
131
+ x0 = max(0, x_c - width2)
132
+ w = width
133
+ if x0 + width > w0:
134
+ x0 = w0 - width
135
+
136
+ if h0 <= height:
137
+ y0 = 0
138
+ h = h0
139
+ else:
140
+ y0 = max(0, y_c - height2)
141
+ h = height
142
+ if y0 + height > h0:
143
+ y0 = h0 - height
144
+
145
+ return (int(x0), int(y0), int(w), int(h))
146
+
147
+ # Prepare conditioning_latents
148
+ @torch.inference_mode()
149
+ def get_image_latents(self, masked_image, mask, vae, scaling_factor):
150
+ processed_image = masked_image.to(vae.device)
151
+ image_latents = vae.encode(processed_image[:, :, :, :3]) * scaling_factor
152
+ processed_mask = 1. - mask[:, None, :, :]
153
+ interpolated_mask = torch.nn.functional.interpolate(
154
+ processed_mask,
155
+ size=(
156
+ image_latents.shape[-2],
157
+ image_latents.shape[-1]
158
+ )
159
+ )
160
+ interpolated_mask = interpolated_mask.to(image_latents.device)
161
+
162
+ conditioning_latents = [image_latents, interpolated_mask]
163
+
164
+ print('BrushNet CL: image_latents shape =', image_latents.shape, 'interpolated_mask shape =',
165
+ interpolated_mask.shape)
166
+
167
+ return conditioning_latents
168
+
169
+ def brushnet_blocks(self, sd):
170
+ brushnet_down_block = 0
171
+ brushnet_mid_block = 0
172
+ brushnet_up_block = 0
173
+ for key in sd:
174
+ if 'brushnet_down_block' in key:
175
+ brushnet_down_block += 1
176
+ if 'brushnet_mid_block' in key:
177
+ brushnet_mid_block += 1
178
+ if 'brushnet_up_block' in key:
179
+ brushnet_up_block += 1
180
+ return (brushnet_down_block, brushnet_mid_block, brushnet_up_block, len(sd))
181
+
182
+ def get_model_type(self, brushnet_file):
183
+ sd = comfy.utils.load_torch_file(brushnet_file)
184
+ brushnet_down_block, brushnet_mid_block, brushnet_up_block, keys = self.brushnet_blocks(sd)
185
+ del sd
186
+ if brushnet_down_block == 24 and brushnet_mid_block == 2 and brushnet_up_block == 30:
187
+ is_SDXL = False
188
+ if keys == 322:
189
+ is_PP = False
190
+ print('BrushNet model type: SD1.5')
191
+ else:
192
+ is_PP = True
193
+ print('PowerPaint model type: SD1.5')
194
+ elif brushnet_down_block == 18 and brushnet_mid_block == 2 and brushnet_up_block == 22:
195
+ print('BrushNet model type: Loading SDXL')
196
+ is_SDXL = True
197
+ is_PP = False
198
+ else:
199
+ raise Exception("Unknown BrushNet model")
200
+ return is_SDXL, is_PP
201
+
202
+ def load_brushnet_model(self, brushnet_file, dtype='float16'):
203
+ is_SDXL, is_PP = self.get_model_type(brushnet_file)
204
+ with init_empty_weights():
205
+ if is_SDXL:
206
+ brushnet_config = BrushNetModel.load_config(brushnet_xl_config_file)
207
+ brushnet_model = BrushNetModel.from_config(brushnet_config)
208
+ elif is_PP:
209
+ brushnet_config = PowerPaintModel.load_config(powerpaint_config_file)
210
+ brushnet_model = PowerPaintModel.from_config(brushnet_config)
211
+ else:
212
+ brushnet_config = BrushNetModel.load_config(brushnet_config_file)
213
+ brushnet_model = BrushNetModel.from_config(brushnet_config)
214
+ if is_PP:
215
+ print("PowerPaint model file:", brushnet_file)
216
+ else:
217
+ print("BrushNet model file:", brushnet_file)
218
+
219
+ if dtype == 'float16':
220
+ torch_dtype = torch.float16
221
+ elif dtype == 'bfloat16':
222
+ torch_dtype = torch.bfloat16
223
+ elif dtype == 'float32':
224
+ torch_dtype = torch.float32
225
+ else:
226
+ torch_dtype = torch.float64
227
+
228
+ brushnet_model = load_checkpoint_and_dispatch(
229
+ brushnet_model,
230
+ brushnet_file,
231
+ device_map="sequential",
232
+ max_memory=None,
233
+ offload_folder=None,
234
+ offload_state_dict=False,
235
+ dtype=torch_dtype,
236
+ force_hooks=False,
237
+ )
238
+
239
+ if is_PP:
240
+ print("PowerPaint model is loaded")
241
+ elif is_SDXL:
242
+ print("BrushNet SDXL model is loaded")
243
+ else:
244
+ print("BrushNet SD1.5 model is loaded")
245
+
246
+ return ({"brushnet": brushnet_model, "SDXL": is_SDXL, "PP": is_PP, "dtype": torch_dtype},)
247
+
248
+ def brushnet_model_update(self, model, vae, image, mask, brushnet, positive, negative, scale, start_at, end_at):
249
+
250
+ is_SDXL, is_PP = self.check_compatibilty(model, brushnet)
251
+
252
+ if is_PP:
253
+ raise Exception("PowerPaint model was loaded, please use PowerPaint node")
254
+
255
+ # Make a copy of the model so that we're not patching it everywhere in the workflow.
256
+ model = model.clone()
257
+
258
+ # prepare image and mask
259
+ # no batches for original image and mask
260
+ masked_image, mask = self.prepare_image(image, mask)
261
+
262
+ batch = masked_image.shape[0]
263
+ width = masked_image.shape[2]
264
+ height = masked_image.shape[1]
265
+
266
+ if hasattr(model.model.model_config, 'latent_format') and hasattr(model.model.model_config.latent_format,
267
+ 'scale_factor'):
268
+ scaling_factor = model.model.model_config.latent_format.scale_factor
269
+ elif is_SDXL:
270
+ scaling_factor = sdxl_scaling_factor
271
+ else:
272
+ scaling_factor = sd15_scaling_factor
273
+
274
+ torch_dtype = brushnet['dtype']
275
+
276
+ # prepare conditioning latents
277
+ conditioning_latents = self.get_image_latents(masked_image, mask, vae, scaling_factor)
278
+ conditioning_latents[0] = conditioning_latents[0].to(dtype=torch_dtype).to(brushnet['brushnet'].device)
279
+ conditioning_latents[1] = conditioning_latents[1].to(dtype=torch_dtype).to(brushnet['brushnet'].device)
280
+
281
+ # unload vae
282
+ del vae
283
+ # for loaded_model in comfy.model_management.current_loaded_models:
284
+ # if type(loaded_model.model.model) in ModelsToUnload:
285
+ # comfy.model_management.current_loaded_models.remove(loaded_model)
286
+ # loaded_model.model_unload()
287
+ # del loaded_model
288
+
289
+ # prepare embeddings
290
+ prompt_embeds = positive[0][0].to(dtype=torch_dtype).to(brushnet['brushnet'].device)
291
+ negative_prompt_embeds = negative[0][0].to(dtype=torch_dtype).to(brushnet['brushnet'].device)
292
+
293
+ max_tokens = max(prompt_embeds.shape[1], negative_prompt_embeds.shape[1])
294
+ if prompt_embeds.shape[1] < max_tokens:
295
+ multiplier = max_tokens // 77 - prompt_embeds.shape[1] // 77
296
+ prompt_embeds = torch.concat([prompt_embeds] + [prompt_embeds[:, -77:, :]] * multiplier, dim=1)
297
+ print('BrushNet: negative prompt more than 75 tokens:', negative_prompt_embeds.shape,
298
+ 'multiplying prompt_embeds')
299
+ if negative_prompt_embeds.shape[1] < max_tokens:
300
+ multiplier = max_tokens // 77 - negative_prompt_embeds.shape[1] // 77
301
+ negative_prompt_embeds = torch.concat(
302
+ [negative_prompt_embeds] + [negative_prompt_embeds[:, -77:, :]] * multiplier, dim=1)
303
+ print('BrushNet: positive prompt more than 75 tokens:', prompt_embeds.shape,
304
+ 'multiplying negative_prompt_embeds')
305
+
306
+ if len(positive[0]) > 1 and 'pooled_output' in positive[0][1] and positive[0][1]['pooled_output'] is not None:
307
+ pooled_prompt_embeds = positive[0][1]['pooled_output'].to(dtype=torch_dtype).to(brushnet['brushnet'].device)
308
+ else:
309
+ print('BrushNet: positive conditioning has not pooled_output')
310
+ if is_SDXL:
311
+ print('BrushNet will not produce correct results')
312
+ pooled_prompt_embeds = torch.empty([2, 1280], device=brushnet['brushnet'].device).to(dtype=torch_dtype)
313
+
314
+ if len(negative[0]) > 1 and 'pooled_output' in negative[0][1] and negative[0][1]['pooled_output'] is not None:
315
+ negative_pooled_prompt_embeds = negative[0][1]['pooled_output'].to(dtype=torch_dtype).to(
316
+ brushnet['brushnet'].device)
317
+ else:
318
+ print('BrushNet: negative conditioning has not pooled_output')
319
+ if is_SDXL:
320
+ print('BrushNet will not produce correct results')
321
+ negative_pooled_prompt_embeds = torch.empty([1, pooled_prompt_embeds.shape[1]],
322
+ device=brushnet['brushnet'].device).to(dtype=torch_dtype)
323
+
324
+ time_ids = torch.FloatTensor([[height, width, 0., 0., height, width]]).to(dtype=torch_dtype).to(
325
+ brushnet['brushnet'].device)
326
+
327
+ if not is_SDXL:
328
+ pooled_prompt_embeds = None
329
+ negative_pooled_prompt_embeds = None
330
+ time_ids = None
331
+
332
+ # apply patch to model
333
+ brushnet_conditioning_scale = scale
334
+ control_guidance_start = start_at
335
+ control_guidance_end = end_at
336
+
337
+ add_brushnet_patch(model,
338
+ brushnet['brushnet'],
339
+ torch_dtype,
340
+ conditioning_latents,
341
+ (brushnet_conditioning_scale, control_guidance_start, control_guidance_end),
342
+ prompt_embeds, negative_prompt_embeds,
343
+ pooled_prompt_embeds, negative_pooled_prompt_embeds, time_ids,
344
+ False)
345
+
346
+ latent = torch.zeros([batch, 4, conditioning_latents[0].shape[2], conditioning_latents[0].shape[3]],
347
+ device=brushnet['brushnet'].device)
348
+
349
+ return (model, positive, negative, {"samples": latent},)
350
+
351
+ #powperpaint
352
+ def load_powerpaint_clip(self, base_clip_file, pp_clip_file):
353
+ pp_clip = comfy.sd.load_clip(ckpt_paths=[base_clip_file])
354
+
355
+ print('PowerPaint base CLIP file: ', base_clip_file)
356
+
357
+ pp_tokenizer = TokenizerWrapper(pp_clip.tokenizer.clip_l.tokenizer)
358
+ pp_text_encoder = pp_clip.patcher.model.clip_l.transformer
359
+
360
+ add_tokens(
361
+ tokenizer=pp_tokenizer,
362
+ text_encoder=pp_text_encoder,
363
+ placeholder_tokens=["P_ctxt", "P_shape", "P_obj"],
364
+ initialize_tokens=["a", "a", "a"],
365
+ num_vectors_per_token=10,
366
+ )
367
+
368
+ pp_text_encoder.load_state_dict(comfy.utils.load_torch_file(pp_clip_file), strict=False)
369
+
370
+ print('PowerPaint CLIP file: ', pp_clip_file)
371
+
372
+ pp_clip.tokenizer.clip_l.tokenizer = pp_tokenizer
373
+ pp_clip.patcher.model.clip_l.transformer = pp_text_encoder
374
+
375
+ return (pp_clip,)
376
+
377
+ def powerpaint_model_update(self, model, vae, image, mask, powerpaint, clip, positive, negative, fitting, function, scale, start_at, end_at, save_memory):
378
+ is_SDXL, is_PP = self.check_compatibilty(model, powerpaint)
379
+ if not is_PP:
380
+ raise Exception("BrushNet model was loaded, please use BrushNet node")
381
+
382
+ # Make a copy of the model so that we're not patching it everywhere in the workflow.
383
+ model = model.clone()
384
+
385
+ # prepare image and mask
386
+ # no batches for original image and mask
387
+ masked_image, mask = self.prepare_image(image, mask)
388
+
389
+ batch = masked_image.shape[0]
390
+ # width = masked_image.shape[2]
391
+ # height = masked_image.shape[1]
392
+
393
+ if hasattr(model.model.model_config, 'latent_format') and hasattr(model.model.model_config.latent_format,
394
+ 'scale_factor'):
395
+ scaling_factor = model.model.model_config.latent_format.scale_factor
396
+ else:
397
+ scaling_factor = sd15_scaling_factor
398
+
399
+ torch_dtype = powerpaint['dtype']
400
+
401
+ # prepare conditioning latents
402
+ conditioning_latents = self.get_image_latents(masked_image, mask, vae, scaling_factor)
403
+ conditioning_latents[0] = conditioning_latents[0].to(dtype=torch_dtype).to(powerpaint['brushnet'].device)
404
+ conditioning_latents[1] = conditioning_latents[1].to(dtype=torch_dtype).to(powerpaint['brushnet'].device)
405
+
406
+ # prepare embeddings
407
+
408
+ if function == "object removal":
409
+ promptA = "P_ctxt"
410
+ promptB = "P_ctxt"
411
+ negative_promptA = "P_obj"
412
+ negative_promptB = "P_obj"
413
+ print('You should add to positive prompt: "empty scene blur"')
414
+ # positive = positive + " empty scene blur"
415
+ elif function == "context aware":
416
+ promptA = "P_ctxt"
417
+ promptB = "P_ctxt"
418
+ negative_promptA = ""
419
+ negative_promptB = ""
420
+ # positive = positive + " empty scene"
421
+ print('You should add to positive prompt: "empty scene"')
422
+ elif function == "shape guided":
423
+ promptA = "P_shape"
424
+ promptB = "P_ctxt"
425
+ negative_promptA = "P_shape"
426
+ negative_promptB = "P_ctxt"
427
+ elif function == "image outpainting":
428
+ promptA = "P_ctxt"
429
+ promptB = "P_ctxt"
430
+ negative_promptA = "P_obj"
431
+ negative_promptB = "P_obj"
432
+ # positive = positive + " empty scene"
433
+ print('You should add to positive prompt: "empty scene"')
434
+ else:
435
+ promptA = "P_obj"
436
+ promptB = "P_obj"
437
+ negative_promptA = "P_obj"
438
+ negative_promptB = "P_obj"
439
+
440
+ tokens = clip.tokenize(promptA)
441
+ prompt_embedsA = clip.encode_from_tokens(tokens, return_pooled=False)
442
+
443
+ tokens = clip.tokenize(negative_promptA)
444
+ negative_prompt_embedsA = clip.encode_from_tokens(tokens, return_pooled=False)
445
+
446
+ tokens = clip.tokenize(promptB)
447
+ prompt_embedsB = clip.encode_from_tokens(tokens, return_pooled=False)
448
+
449
+ tokens = clip.tokenize(negative_promptB)
450
+ negative_prompt_embedsB = clip.encode_from_tokens(tokens, return_pooled=False)
451
+
452
+ prompt_embeds_pp = (prompt_embedsA * fitting + (1.0 - fitting) * prompt_embedsB).to(dtype=torch_dtype).to(
453
+ powerpaint['brushnet'].device)
454
+ negative_prompt_embeds_pp = (negative_prompt_embedsA * fitting + (1.0 - fitting) * negative_prompt_embedsB).to(
455
+ dtype=torch_dtype).to(powerpaint['brushnet'].device)
456
+
457
+ # unload vae and CLIPs
458
+ del vae
459
+ del clip
460
+ # for loaded_model in comfy.model_management.current_loaded_models:
461
+ # if type(loaded_model.model.model) in ModelsToUnload:
462
+ # comfy.model_management.current_loaded_models.remove(loaded_model)
463
+ # loaded_model.model_unload()
464
+ # del loaded_model
465
+
466
+ # apply patch to model
467
+
468
+ brushnet_conditioning_scale = scale
469
+ control_guidance_start = start_at
470
+ control_guidance_end = end_at
471
+
472
+ if save_memory != 'none':
473
+ powerpaint['brushnet'].set_attention_slice(save_memory)
474
+
475
+ add_brushnet_patch(model,
476
+ powerpaint['brushnet'],
477
+ torch_dtype,
478
+ conditioning_latents,
479
+ (brushnet_conditioning_scale, control_guidance_start, control_guidance_end),
480
+ negative_prompt_embeds_pp, prompt_embeds_pp,
481
+ None, None, None,
482
+ False)
483
+
484
+ latent = torch.zeros([batch, 4, conditioning_latents[0].shape[2], conditioning_latents[0].shape[3]],
485
+ device=powerpaint['brushnet'].device)
486
+
487
+ return (model, positive, negative, {"samples": latent},)
488
+ @torch.inference_mode()
489
+ def brushnet_inference(x, timesteps, transformer_options, debug):
490
+ if 'model_patch' not in transformer_options:
491
+ print('BrushNet inference: there is no model_patch key in transformer_options')
492
+ return ([], 0, [])
493
+ mp = transformer_options['model_patch']
494
+ if 'brushnet' not in mp:
495
+ print('BrushNet inference: there is no brushnet key in mdel_patch')
496
+ return ([], 0, [])
497
+ bo = mp['brushnet']
498
+ if 'model' not in bo:
499
+ print('BrushNet inference: there is no model key in brushnet')
500
+ return ([], 0, [])
501
+ brushnet = bo['model']
502
+ if not (isinstance(brushnet, BrushNetModel) or isinstance(brushnet, PowerPaintModel)):
503
+ print('BrushNet model is not a BrushNetModel class')
504
+ return ([], 0, [])
505
+
506
+ torch_dtype = bo['dtype']
507
+ cl_list = bo['latents']
508
+ brushnet_conditioning_scale, control_guidance_start, control_guidance_end = bo['controls']
509
+ pe = bo['prompt_embeds']
510
+ npe = bo['negative_prompt_embeds']
511
+ ppe, nppe, time_ids = bo['add_embeds']
512
+
513
+ #do_classifier_free_guidance = mp['free_guidance']
514
+ do_classifier_free_guidance = len(transformer_options['cond_or_uncond']) > 1
515
+
516
+ x = x.detach().clone()
517
+ x = x.to(torch_dtype).to(brushnet.device)
518
+
519
+ timesteps = timesteps.detach().clone()
520
+ timesteps = timesteps.to(torch_dtype).to(brushnet.device)
521
+
522
+ total_steps = mp['total_steps']
523
+ step = mp['step']
524
+
525
+ added_cond_kwargs = {}
526
+
527
+ if do_classifier_free_guidance and step == 0:
528
+ print('BrushNet inference: do_classifier_free_guidance is True')
529
+
530
+ sub_idx = None
531
+ if 'ad_params' in transformer_options and 'sub_idxs' in transformer_options['ad_params']:
532
+ sub_idx = transformer_options['ad_params']['sub_idxs']
533
+
534
+ # we have batch input images
535
+ batch = cl_list[0].shape[0]
536
+ # we have incoming latents
537
+ latents_incoming = x.shape[0]
538
+ # and we already got some
539
+ latents_got = bo['latent_id']
540
+ if step == 0 or batch > 1:
541
+ print('BrushNet inference, step = %d: image batch = %d, got %d latents, starting from %d' \
542
+ % (step, batch, latents_incoming, latents_got))
543
+
544
+ image_latents = []
545
+ masks = []
546
+ prompt_embeds = []
547
+ negative_prompt_embeds = []
548
+ pooled_prompt_embeds = []
549
+ negative_pooled_prompt_embeds = []
550
+ if sub_idx:
551
+ # AnimateDiff indexes detected
552
+ if step == 0:
553
+ print('BrushNet inference: AnimateDiff indexes detected and applied')
554
+
555
+ batch = len(sub_idx)
556
+
557
+ if do_classifier_free_guidance:
558
+ for i in sub_idx:
559
+ image_latents.append(cl_list[0][i][None,:,:,:])
560
+ masks.append(cl_list[1][i][None,:,:,:])
561
+ prompt_embeds.append(pe)
562
+ negative_prompt_embeds.append(npe)
563
+ pooled_prompt_embeds.append(ppe)
564
+ negative_pooled_prompt_embeds.append(nppe)
565
+ for i in sub_idx:
566
+ image_latents.append(cl_list[0][i][None,:,:,:])
567
+ masks.append(cl_list[1][i][None,:,:,:])
568
+ else:
569
+ for i in sub_idx:
570
+ image_latents.append(cl_list[0][i][None,:,:,:])
571
+ masks.append(cl_list[1][i][None,:,:,:])
572
+ prompt_embeds.append(pe)
573
+ pooled_prompt_embeds.append(ppe)
574
+ else:
575
+ # do_classifier_free_guidance = 2 passes, 1st pass is cond, 2nd is uncond
576
+ continue_batch = True
577
+ for i in range(latents_incoming):
578
+ number = latents_got + i
579
+ if number < batch:
580
+ # 1st pass, cond
581
+ image_latents.append(cl_list[0][number][None,:,:,:])
582
+ masks.append(cl_list[1][number][None,:,:,:])
583
+ prompt_embeds.append(pe)
584
+ pooled_prompt_embeds.append(ppe)
585
+ elif do_classifier_free_guidance and number < batch * 2:
586
+ # 2nd pass, uncond
587
+ image_latents.append(cl_list[0][number-batch][None,:,:,:])
588
+ masks.append(cl_list[1][number-batch][None,:,:,:])
589
+ negative_prompt_embeds.append(npe)
590
+ negative_pooled_prompt_embeds.append(nppe)
591
+ else:
592
+ # latent batch
593
+ image_latents.append(cl_list[0][0][None,:,:,:])
594
+ masks.append(cl_list[1][0][None,:,:,:])
595
+ prompt_embeds.append(pe)
596
+ pooled_prompt_embeds.append(ppe)
597
+ latents_got = -i
598
+ continue_batch = False
599
+
600
+ if continue_batch:
601
+ # we don't have full batch yet
602
+ if do_classifier_free_guidance:
603
+ if number < batch * 2 - 1:
604
+ bo['latent_id'] = number + 1
605
+ else:
606
+ bo['latent_id'] = 0
607
+ else:
608
+ if number < batch - 1:
609
+ bo['latent_id'] = number + 1
610
+ else:
611
+ bo['latent_id'] = 0
612
+ else:
613
+ bo['latent_id'] = 0
614
+
615
+ cl = []
616
+ for il, m in zip(image_latents, masks):
617
+ cl.append(torch.concat([il, m], dim=1))
618
+ cl2apply = torch.concat(cl, dim=0)
619
+
620
+ conditioning_latents = cl2apply.to(torch_dtype).to(brushnet.device)
621
+
622
+ prompt_embeds.extend(negative_prompt_embeds)
623
+ prompt_embeds = torch.concat(prompt_embeds, dim=0).to(torch_dtype).to(brushnet.device)
624
+
625
+ if ppe is not None:
626
+ added_cond_kwargs = {}
627
+ added_cond_kwargs['time_ids'] = torch.concat([time_ids] * latents_incoming, dim = 0).to(torch_dtype).to(brushnet.device)
628
+
629
+ pooled_prompt_embeds.extend(negative_pooled_prompt_embeds)
630
+ pooled_prompt_embeds = torch.concat(pooled_prompt_embeds, dim=0).to(torch_dtype).to(brushnet.device)
631
+ added_cond_kwargs['text_embeds'] = pooled_prompt_embeds
632
+ else:
633
+ added_cond_kwargs = None
634
+
635
+ if x.shape[2] != conditioning_latents.shape[2] or x.shape[3] != conditioning_latents.shape[3]:
636
+ if step == 0:
637
+ print('BrushNet inference: image', conditioning_latents.shape, 'and latent', x.shape, 'have different size, resizing image')
638
+ conditioning_latents = torch.nn.functional.interpolate(
639
+ conditioning_latents, size=(
640
+ x.shape[2],
641
+ x.shape[3],
642
+ ), mode='bicubic',
643
+ ).to(torch_dtype).to(brushnet.device)
644
+
645
+ if step == 0:
646
+ print('BrushNet inference: sample', x.shape, ', CL', conditioning_latents.shape, 'dtype', torch_dtype)
647
+
648
+ if debug: print('BrushNet: step =', step)
649
+
650
+ if step < control_guidance_start or step > control_guidance_end:
651
+ cond_scale = 0.0
652
+ else:
653
+ cond_scale = brushnet_conditioning_scale
654
+
655
+ return brushnet(x,
656
+ encoder_hidden_states=prompt_embeds,
657
+ brushnet_cond=conditioning_latents,
658
+ timestep = timesteps,
659
+ conditioning_scale=cond_scale,
660
+ guess_mode=False,
661
+ added_cond_kwargs=added_cond_kwargs,
662
+ return_dict=False,
663
+ debug=debug,
664
+ )
665
+
666
+ def add_brushnet_patch(model, brushnet, torch_dtype, conditioning_latents,
667
+ controls,
668
+ prompt_embeds, negative_prompt_embeds,
669
+ pooled_prompt_embeds, negative_pooled_prompt_embeds, time_ids,
670
+ debug):
671
+
672
+ is_SDXL = isinstance(model.model.model_config, comfy.supported_models.SDXL)
673
+
674
+ if model.model.model_config.custom_operations is None:
675
+ fp8 = model.model.model_config.optimizations.get("fp8", model.model.model_config.scaled_fp8 is not None)
676
+ operations = comfy.ops.pick_operations(model.model.model_config.unet_config.get("dtype", None), model.model.manual_cast_dtype,
677
+ fp8_optimizations=fp8, scaled_fp8=model.model.model_config.scaled_fp8)
678
+ else:
679
+ # such as gguf
680
+ operations = model.model.model_config.custom_operations
681
+
682
+ if is_SDXL:
683
+ input_blocks = [[0, operations.Conv2d],
684
+ [1, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock],
685
+ [2, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock],
686
+ [3, comfy.ldm.modules.diffusionmodules.openaimodel.Downsample],
687
+ [4, comfy.ldm.modules.attention.SpatialTransformer],
688
+ [5, comfy.ldm.modules.attention.SpatialTransformer],
689
+ [6, comfy.ldm.modules.diffusionmodules.openaimodel.Downsample],
690
+ [7, comfy.ldm.modules.attention.SpatialTransformer],
691
+ [8, comfy.ldm.modules.attention.SpatialTransformer]]
692
+ middle_block = [0, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock]
693
+ output_blocks = [[0, comfy.ldm.modules.attention.SpatialTransformer],
694
+ [1, comfy.ldm.modules.attention.SpatialTransformer],
695
+ [2, comfy.ldm.modules.attention.SpatialTransformer],
696
+ [2, comfy.ldm.modules.diffusionmodules.openaimodel.Upsample],
697
+ [3, comfy.ldm.modules.attention.SpatialTransformer],
698
+ [4, comfy.ldm.modules.attention.SpatialTransformer],
699
+ [5, comfy.ldm.modules.attention.SpatialTransformer],
700
+ [5, comfy.ldm.modules.diffusionmodules.openaimodel.Upsample],
701
+ [6, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock],
702
+ [7, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock],
703
+ [8, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock]]
704
+ else:
705
+ input_blocks = [[0, operations.Conv2d],
706
+ [1, comfy.ldm.modules.attention.SpatialTransformer],
707
+ [2, comfy.ldm.modules.attention.SpatialTransformer],
708
+ [3, comfy.ldm.modules.diffusionmodules.openaimodel.Downsample],
709
+ [4, comfy.ldm.modules.attention.SpatialTransformer],
710
+ [5, comfy.ldm.modules.attention.SpatialTransformer],
711
+ [6, comfy.ldm.modules.diffusionmodules.openaimodel.Downsample],
712
+ [7, comfy.ldm.modules.attention.SpatialTransformer],
713
+ [8, comfy.ldm.modules.attention.SpatialTransformer],
714
+ [9, comfy.ldm.modules.diffusionmodules.openaimodel.Downsample],
715
+ [10, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock],
716
+ [11, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock]]
717
+ middle_block = [0, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock]
718
+ output_blocks = [[0, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock],
719
+ [1, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock],
720
+ [2, comfy.ldm.modules.diffusionmodules.openaimodel.ResBlock],
721
+ [2, comfy.ldm.modules.diffusionmodules.openaimodel.Upsample],
722
+ [3, comfy.ldm.modules.attention.SpatialTransformer],
723
+ [4, comfy.ldm.modules.attention.SpatialTransformer],
724
+ [5, comfy.ldm.modules.attention.SpatialTransformer],
725
+ [5, comfy.ldm.modules.diffusionmodules.openaimodel.Upsample],
726
+ [6, comfy.ldm.modules.attention.SpatialTransformer],
727
+ [7, comfy.ldm.modules.attention.SpatialTransformer],
728
+ [8, comfy.ldm.modules.attention.SpatialTransformer],
729
+ [8, comfy.ldm.modules.diffusionmodules.openaimodel.Upsample],
730
+ [9, comfy.ldm.modules.attention.SpatialTransformer],
731
+ [10, comfy.ldm.modules.attention.SpatialTransformer],
732
+ [11, comfy.ldm.modules.attention.SpatialTransformer]]
733
+
734
+ def last_layer_index(block, tp):
735
+ layer_list = []
736
+ for layer in block:
737
+ layer_list.append(type(layer))
738
+ layer_list.reverse()
739
+ if tp not in layer_list:
740
+ return -1, layer_list.reverse()
741
+ return len(layer_list) - 1 - layer_list.index(tp), layer_list
742
+
743
+ def brushnet_forward(model, x, timesteps, transformer_options, control):
744
+ if 'brushnet' not in transformer_options['model_patch']:
745
+ input_samples = []
746
+ mid_sample = 0
747
+ output_samples = []
748
+ else:
749
+ # brushnet inference
750
+ input_samples, mid_sample, output_samples = brushnet_inference(x, timesteps, transformer_options, debug)
751
+
752
+ # give additional samples to blocks
753
+ for i, tp in input_blocks:
754
+ idx, layer_list = last_layer_index(model.input_blocks[i], tp)
755
+ if idx < 0:
756
+ print("BrushNet can't find", tp, "layer in", i, "input block:", layer_list)
757
+ continue
758
+ model.input_blocks[i][idx].add_sample_after = input_samples.pop(0) if input_samples else 0
759
+
760
+ idx, layer_list = last_layer_index(model.middle_block, middle_block[1])
761
+ if idx < 0:
762
+ print("BrushNet can't find", middle_block[1], "layer in middle block", layer_list)
763
+ model.middle_block[idx].add_sample_after = mid_sample
764
+
765
+ for i, tp in output_blocks:
766
+ idx, layer_list = last_layer_index(model.output_blocks[i], tp)
767
+ if idx < 0:
768
+ print("BrushNet can't find", tp, "layer in", i, "outnput block:", layer_list)
769
+ continue
770
+ model.output_blocks[i][idx].add_sample_after = output_samples.pop(0) if output_samples else 0
771
+
772
+ patch_model_function_wrapper(model, brushnet_forward)
773
+
774
+ to = add_model_patch_option(model)
775
+ mp = to['model_patch']
776
+ if 'brushnet' not in mp:
777
+ mp['brushnet'] = {}
778
+ bo = mp['brushnet']
779
+
780
+ bo['model'] = brushnet
781
+ bo['dtype'] = torch_dtype
782
+ bo['latents'] = conditioning_latents
783
+ bo['controls'] = controls
784
+ bo['prompt_embeds'] = prompt_embeds
785
+ bo['negative_prompt_embeds'] = negative_prompt_embeds
786
+ bo['add_embeds'] = (pooled_prompt_embeds, negative_pooled_prompt_embeds, time_ids)
787
+ bo['latent_id'] = 0
788
+
789
+ # patch layers `forward` so we can apply brushnet
790
+ def forward_patched_by_brushnet(self, x, *args, **kwargs):
791
+ h = self.original_forward(x, *args, **kwargs)
792
+ if hasattr(self, 'add_sample_after') and type(self):
793
+ to_add = self.add_sample_after
794
+ if torch.is_tensor(to_add):
795
+ # interpolate due to RAUNet
796
+ if h.shape[2] != to_add.shape[2] or h.shape[3] != to_add.shape[3]:
797
+ to_add = torch.nn.functional.interpolate(to_add, size=(h.shape[2], h.shape[3]), mode='bicubic')
798
+ h += to_add.to(h.dtype).to(h.device)
799
+ else:
800
+ h += self.add_sample_after
801
+ self.add_sample_after = 0
802
+ return h
803
+
804
+ for i, block in enumerate(model.model.diffusion_model.input_blocks):
805
+ for j, layer in enumerate(block):
806
+ if not hasattr(layer, 'original_forward'):
807
+ layer.original_forward = layer.forward
808
+ layer.forward = types.MethodType(forward_patched_by_brushnet, layer)
809
+ layer.add_sample_after = 0
810
+
811
+ for j, layer in enumerate(model.model.diffusion_model.middle_block):
812
+ if not hasattr(layer, 'original_forward'):
813
+ layer.original_forward = layer.forward
814
+ layer.forward = types.MethodType(forward_patched_by_brushnet, layer)
815
+ layer.add_sample_after = 0
816
+
817
+ for i, block in enumerate(model.model.diffusion_model.output_blocks):
818
+ for j, layer in enumerate(block):
819
+ if not hasattr(layer, 'original_forward'):
820
+ layer.original_forward = layer.forward
821
+ layer.forward = types.MethodType(forward_patched_by_brushnet, layer)
822
+ layer.add_sample_after = 0
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (41.5 kB). View file
 
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/model.cpython-312.pyc ADDED
Binary file (63.5 kB). View file
 
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/model_patch.cpython-312.pyc ADDED
Binary file (8 kB). View file
 
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/powerpaint_utils.cpython-312.pyc ADDED
Binary file (22.8 kB). View file
 
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/unet_2d_blocks.cpython-312.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1148210ee8f1bc384cd3fbefb1425591976f0e14d66094d494abf175fdf6f255
3
+ size 111447
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/unet_2d_condition.cpython-312.pyc ADDED
Binary file (59.9 kB). View file
 
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/config/brushnet.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "BrushNetModel",
3
+ "_diffusers_version": "0.27.0.dev0",
4
+ "_name_or_path": "runs/logs/brushnet_randommask/checkpoint-100000",
5
+ "act_fn": "silu",
6
+ "addition_embed_type": null,
7
+ "addition_embed_type_num_heads": 64,
8
+ "addition_time_embed_dim": null,
9
+ "attention_head_dim": 8,
10
+ "block_out_channels": [
11
+ 320,
12
+ 640,
13
+ 1280,
14
+ 1280
15
+ ],
16
+ "brushnet_conditioning_channel_order": "rgb",
17
+ "class_embed_type": null,
18
+ "conditioning_channels": 5,
19
+ "conditioning_embedding_out_channels": [
20
+ 16,
21
+ 32,
22
+ 96,
23
+ 256
24
+ ],
25
+ "cross_attention_dim": 768,
26
+ "down_block_types": [
27
+ "DownBlock2D",
28
+ "DownBlock2D",
29
+ "DownBlock2D",
30
+ "DownBlock2D"
31
+ ],
32
+ "downsample_padding": 1,
33
+ "encoder_hid_dim": null,
34
+ "encoder_hid_dim_type": null,
35
+ "flip_sin_to_cos": true,
36
+ "freq_shift": 0,
37
+ "global_pool_conditions": false,
38
+ "in_channels": 4,
39
+ "layers_per_block": 2,
40
+ "mid_block_scale_factor": 1,
41
+ "mid_block_type": "MidBlock2D",
42
+ "norm_eps": 1e-05,
43
+ "norm_num_groups": 32,
44
+ "num_attention_heads": null,
45
+ "num_class_embeds": null,
46
+ "only_cross_attention": false,
47
+ "projection_class_embeddings_input_dim": null,
48
+ "resnet_time_scale_shift": "default",
49
+ "transformer_layers_per_block": 1,
50
+ "up_block_types": [
51
+ "UpBlock2D",
52
+ "UpBlock2D",
53
+ "UpBlock2D",
54
+ "UpBlock2D"
55
+ ],
56
+ "upcast_attention": false,
57
+ "use_linear_projection": false
58
+ }
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/config/brushnet_xl.json ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "BrushNetModel",
3
+ "_diffusers_version": "0.27.0.dev0",
4
+ "_name_or_path": "runs/logs/brushnetsdxl_randommask/checkpoint-80000",
5
+ "act_fn": "silu",
6
+ "addition_embed_type": "text_time",
7
+ "addition_embed_type_num_heads": 64,
8
+ "addition_time_embed_dim": 256,
9
+ "attention_head_dim": [
10
+ 5,
11
+ 10,
12
+ 20
13
+ ],
14
+ "block_out_channels": [
15
+ 320,
16
+ 640,
17
+ 1280
18
+ ],
19
+ "brushnet_conditioning_channel_order": "rgb",
20
+ "class_embed_type": null,
21
+ "conditioning_channels": 5,
22
+ "conditioning_embedding_out_channels": [
23
+ 16,
24
+ 32,
25
+ 96,
26
+ 256
27
+ ],
28
+ "cross_attention_dim": 2048,
29
+ "down_block_types": [
30
+ "DownBlock2D",
31
+ "DownBlock2D",
32
+ "DownBlock2D"
33
+ ],
34
+ "downsample_padding": 1,
35
+ "encoder_hid_dim": null,
36
+ "encoder_hid_dim_type": null,
37
+ "flip_sin_to_cos": true,
38
+ "freq_shift": 0,
39
+ "global_pool_conditions": false,
40
+ "in_channels": 4,
41
+ "layers_per_block": 2,
42
+ "mid_block_scale_factor": 1,
43
+ "mid_block_type": "MidBlock2D",
44
+ "norm_eps": 1e-05,
45
+ "norm_num_groups": 32,
46
+ "num_attention_heads": null,
47
+ "num_class_embeds": null,
48
+ "only_cross_attention": false,
49
+ "projection_class_embeddings_input_dim": 2816,
50
+ "resnet_time_scale_shift": "default",
51
+ "transformer_layers_per_block": [
52
+ 1,
53
+ 2,
54
+ 10
55
+ ],
56
+ "up_block_types": [
57
+ "UpBlock2D",
58
+ "UpBlock2D",
59
+ "UpBlock2D"
60
+ ],
61
+ "upcast_attention": null,
62
+ "use_linear_projection": true
63
+ }
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/config/powerpaint.json ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "BrushNetModel",
3
+ "_diffusers_version": "0.27.2",
4
+ "act_fn": "silu",
5
+ "addition_embed_type": null,
6
+ "addition_embed_type_num_heads": 64,
7
+ "addition_time_embed_dim": null,
8
+ "attention_head_dim": 8,
9
+ "block_out_channels": [
10
+ 320,
11
+ 640,
12
+ 1280,
13
+ 1280
14
+ ],
15
+ "brushnet_conditioning_channel_order": "rgb",
16
+ "class_embed_type": null,
17
+ "conditioning_channels": 5,
18
+ "conditioning_embedding_out_channels": [
19
+ 16,
20
+ 32,
21
+ 96,
22
+ 256
23
+ ],
24
+ "cross_attention_dim": 768,
25
+ "down_block_types": [
26
+ "CrossAttnDownBlock2D",
27
+ "CrossAttnDownBlock2D",
28
+ "CrossAttnDownBlock2D",
29
+ "DownBlock2D"
30
+ ],
31
+ "downsample_padding": 1,
32
+ "encoder_hid_dim": null,
33
+ "encoder_hid_dim_type": null,
34
+ "flip_sin_to_cos": true,
35
+ "freq_shift": 0,
36
+ "global_pool_conditions": false,
37
+ "in_channels": 4,
38
+ "layers_per_block": 2,
39
+ "mid_block_scale_factor": 1,
40
+ "mid_block_type": "UNetMidBlock2DCrossAttn",
41
+ "norm_eps": 1e-05,
42
+ "norm_num_groups": 32,
43
+ "num_attention_heads": null,
44
+ "num_class_embeds": null,
45
+ "only_cross_attention": false,
46
+ "projection_class_embeddings_input_dim": null,
47
+ "resnet_time_scale_shift": "default",
48
+ "transformer_layers_per_block": 1,
49
+ "up_block_types": [
50
+ "UpBlock2D",
51
+ "CrossAttnUpBlock2D",
52
+ "CrossAttnUpBlock2D",
53
+ "CrossAttnUpBlock2D"
54
+ ],
55
+ "upcast_attention": false,
56
+ "use_linear_projection": false
57
+ }
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/model.py ADDED
@@ -0,0 +1,1688 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+ from typing import Any, Dict, List, Optional, Tuple, Union
3
+
4
+ import torch
5
+ from torch import nn
6
+
7
+ from ...libs.utils import install_package
8
+ try:
9
+ install_package("diffusers", "0.27.2", True, "0.25.0")
10
+
11
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
12
+ from diffusers.utils import BaseOutput, logging
13
+ from diffusers.models.attention_processor import (
14
+ ADDED_KV_ATTENTION_PROCESSORS,
15
+ CROSS_ATTENTION_PROCESSORS,
16
+ AttentionProcessor,
17
+ AttnAddedKVProcessor,
18
+ AttnProcessor,
19
+ )
20
+ from diffusers.models.embeddings import TextImageProjection, TextImageTimeEmbedding, TextTimeEmbedding, TimestepEmbedding, Timesteps
21
+ from diffusers.models.modeling_utils import ModelMixin
22
+ from diffusers.models.resnet import ResnetBlock2D
23
+ from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel
24
+
25
+ from diffusers.models.transformers.dual_transformer_2d import DualTransformer2DModel
26
+ from diffusers.models.transformers.transformer_2d import Transformer2DModel
27
+
28
+ from .unet_2d_blocks import (
29
+ CrossAttnDownBlock2D,
30
+ DownBlock2D,
31
+ get_down_block,
32
+ get_mid_block,
33
+ get_up_block,
34
+ )
35
+
36
+ from .unet_2d_condition import UNet2DConditionModel
37
+
38
+ logger = logging.get_logger(__name__)
39
+
40
+ def zero_module(module):
41
+ for p in module.parameters():
42
+ nn.init.zeros_(p)
43
+ return module
44
+
45
+ @dataclass
46
+ class BrushNetOutput(BaseOutput):
47
+
48
+ up_block_res_samples: Tuple[torch.Tensor]
49
+ down_block_res_samples: Tuple[torch.Tensor]
50
+ mid_block_res_sample: torch.Tensor
51
+
52
+ # BrushNetModel
53
+ class BrushNetModel(ModelMixin, ConfigMixin):
54
+ """A BrushNet model."""
55
+ _supports_gradient_checkpointing = True
56
+
57
+ @register_to_config
58
+ def __init__(
59
+ self,
60
+ in_channels: int = 4,
61
+ conditioning_channels: int = 5,
62
+ flip_sin_to_cos: bool = True,
63
+ freq_shift: int = 0,
64
+ down_block_types: Tuple[str, ...] = (
65
+ "DownBlock2D",
66
+ "DownBlock2D",
67
+ "DownBlock2D",
68
+ "DownBlock2D",
69
+ ),
70
+ mid_block_type: Optional[str] = "UNetMidBlock2D",
71
+ up_block_types: Tuple[str, ...] = (
72
+ "UpBlock2D",
73
+ "UpBlock2D",
74
+ "UpBlock2D",
75
+ "UpBlock2D",
76
+ ),
77
+ only_cross_attention: Union[bool, Tuple[bool]] = False,
78
+ block_out_channels: Tuple[int, ...] = (320, 640, 1280, 1280),
79
+ layers_per_block: int = 2,
80
+ downsample_padding: int = 1,
81
+ mid_block_scale_factor: float = 1,
82
+ act_fn: str = "silu",
83
+ norm_num_groups: Optional[int] = 32,
84
+ norm_eps: float = 1e-5,
85
+ cross_attention_dim: int = 1280,
86
+ transformer_layers_per_block: Union[int, Tuple[int, ...]] = 1,
87
+ encoder_hid_dim: Optional[int] = None,
88
+ encoder_hid_dim_type: Optional[str] = None,
89
+ attention_head_dim: Union[int, Tuple[int, ...]] = 8,
90
+ num_attention_heads: Optional[Union[int, Tuple[int, ...]]] = None,
91
+ use_linear_projection: bool = False,
92
+ class_embed_type: Optional[str] = None,
93
+ addition_embed_type: Optional[str] = None,
94
+ addition_time_embed_dim: Optional[int] = None,
95
+ num_class_embeds: Optional[int] = None,
96
+ upcast_attention: bool = False,
97
+ resnet_time_scale_shift: str = "default",
98
+ projection_class_embeddings_input_dim: Optional[int] = None,
99
+ brushnet_conditioning_channel_order: str = "rgb",
100
+ conditioning_embedding_out_channels: Optional[Tuple[int, ...]] = (16, 32, 96, 256),
101
+ global_pool_conditions: bool = False,
102
+ addition_embed_type_num_heads: int = 64,
103
+ ):
104
+ super().__init__()
105
+
106
+ # If `num_attention_heads` is not defined (which is the case for most models)
107
+ # it will default to `attention_head_dim`. This looks weird upon first reading it and it is.
108
+ # The reason for this behavior is to correct for incorrectly named variables that were introduced
109
+ # when this library was created. The incorrect naming was only discovered much later in https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131
110
+ # Changing `attention_head_dim` to `num_attention_heads` for 40,000+ configurations is too backwards breaking
111
+ # which is why we correct for the naming here.
112
+ num_attention_heads = num_attention_heads or attention_head_dim
113
+
114
+ # Check inputs
115
+ if len(down_block_types) != len(up_block_types):
116
+ raise ValueError(
117
+ f"Must provide the same number of `down_block_types` as `up_block_types`. `down_block_types`: {down_block_types}. `up_block_types`: {up_block_types}."
118
+ )
119
+
120
+ if len(block_out_channels) != len(down_block_types):
121
+ raise ValueError(
122
+ f"Must provide the same number of `block_out_channels` as `down_block_types`. `block_out_channels`: {block_out_channels}. `down_block_types`: {down_block_types}."
123
+ )
124
+
125
+ if not isinstance(only_cross_attention, bool) and len(only_cross_attention) != len(down_block_types):
126
+ raise ValueError(
127
+ f"Must provide the same number of `only_cross_attention` as `down_block_types`. `only_cross_attention`: {only_cross_attention}. `down_block_types`: {down_block_types}."
128
+ )
129
+
130
+ if not isinstance(num_attention_heads, int) and len(num_attention_heads) != len(down_block_types):
131
+ raise ValueError(
132
+ f"Must provide the same number of `num_attention_heads` as `down_block_types`. `num_attention_heads`: {num_attention_heads}. `down_block_types`: {down_block_types}."
133
+ )
134
+
135
+ if isinstance(transformer_layers_per_block, int):
136
+ transformer_layers_per_block = [transformer_layers_per_block] * len(down_block_types)
137
+
138
+ # input
139
+ conv_in_kernel = 3
140
+ conv_in_padding = (conv_in_kernel - 1) // 2
141
+ self.conv_in_condition = nn.Conv2d(
142
+ in_channels + conditioning_channels, block_out_channels[0], kernel_size=conv_in_kernel,
143
+ padding=conv_in_padding
144
+ )
145
+
146
+ # time
147
+ time_embed_dim = block_out_channels[0] * 4
148
+ self.time_proj = Timesteps(block_out_channels[0], flip_sin_to_cos, freq_shift)
149
+ timestep_input_dim = block_out_channels[0]
150
+ self.time_embedding = TimestepEmbedding(
151
+ timestep_input_dim,
152
+ time_embed_dim,
153
+ act_fn=act_fn,
154
+ )
155
+
156
+ if encoder_hid_dim_type is None and encoder_hid_dim is not None:
157
+ encoder_hid_dim_type = "text_proj"
158
+ self.register_to_config(encoder_hid_dim_type=encoder_hid_dim_type)
159
+ print("encoder_hid_dim_type defaults to 'text_proj' as `encoder_hid_dim` is defined.")
160
+
161
+ if encoder_hid_dim is None and encoder_hid_dim_type is not None:
162
+ raise ValueError(
163
+ f"`encoder_hid_dim` has to be defined when `encoder_hid_dim_type` is set to {encoder_hid_dim_type}."
164
+ )
165
+
166
+ if encoder_hid_dim_type == "text_proj":
167
+ self.encoder_hid_proj = nn.Linear(encoder_hid_dim, cross_attention_dim)
168
+ elif encoder_hid_dim_type == "text_image_proj":
169
+ # image_embed_dim DOESN'T have to be `cross_attention_dim`. To not clutter the __init__ too much
170
+ # they are set to `cross_attention_dim` here as this is exactly the required dimension for the currently only use
171
+ # case when `addition_embed_type == "text_image_proj"` (Kadinsky 2.1)`
172
+ self.encoder_hid_proj = TextImageProjection(
173
+ text_embed_dim=encoder_hid_dim,
174
+ image_embed_dim=cross_attention_dim,
175
+ cross_attention_dim=cross_attention_dim,
176
+ )
177
+
178
+ elif encoder_hid_dim_type is not None:
179
+ raise ValueError(
180
+ f"encoder_hid_dim_type: {encoder_hid_dim_type} must be None, 'text_proj' or 'text_image_proj'."
181
+ )
182
+ else:
183
+ self.encoder_hid_proj = None
184
+
185
+ # class embedding
186
+ if class_embed_type is None and num_class_embeds is not None:
187
+ self.class_embedding = nn.Embedding(num_class_embeds, time_embed_dim)
188
+ elif class_embed_type == "timestep":
189
+ self.class_embedding = TimestepEmbedding(timestep_input_dim, time_embed_dim)
190
+ elif class_embed_type == "identity":
191
+ self.class_embedding = nn.Identity(time_embed_dim, time_embed_dim)
192
+ elif class_embed_type == "projection":
193
+ if projection_class_embeddings_input_dim is None:
194
+ raise ValueError(
195
+ "`class_embed_type`: 'projection' requires `projection_class_embeddings_input_dim` be set"
196
+ )
197
+ # The projection `class_embed_type` is the same as the timestep `class_embed_type` except
198
+ # 1. the `class_labels` inputs are not first converted to sinusoidal embeddings
199
+ # 2. it projects from an arbitrary input dimension.
200
+ #
201
+ # Note that `TimestepEmbedding` is quite general, being mainly linear layers and activations.
202
+ # When used for embedding actual timesteps, the timesteps are first converted to sinusoidal embeddings.
203
+ # As a result, `TimestepEmbedding` can be passed arbitrary vectors.
204
+ self.class_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
205
+ else:
206
+ self.class_embedding = None
207
+
208
+ if addition_embed_type == "text":
209
+ if encoder_hid_dim is not None:
210
+ text_time_embedding_from_dim = encoder_hid_dim
211
+ else:
212
+ text_time_embedding_from_dim = cross_attention_dim
213
+
214
+ self.add_embedding = TextTimeEmbedding(
215
+ text_time_embedding_from_dim, time_embed_dim, num_heads=addition_embed_type_num_heads
216
+ )
217
+ elif addition_embed_type == "text_image":
218
+ # text_embed_dim and image_embed_dim DON'T have to be `cross_attention_dim`. To not clutter the __init__ too much
219
+ # they are set to `cross_attention_dim` here as this is exactly the required dimension for the currently only use
220
+ # case when `addition_embed_type == "text_image"` (Kadinsky 2.1)`
221
+ self.add_embedding = TextImageTimeEmbedding(
222
+ text_embed_dim=cross_attention_dim, image_embed_dim=cross_attention_dim,
223
+ time_embed_dim=time_embed_dim
224
+ )
225
+ elif addition_embed_type == "text_time":
226
+ self.add_time_proj = Timesteps(addition_time_embed_dim, flip_sin_to_cos, freq_shift)
227
+ self.add_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
228
+
229
+ elif addition_embed_type is not None:
230
+ raise ValueError(f"addition_embed_type: {addition_embed_type} must be None, 'text' or 'text_image'.")
231
+
232
+ self.down_blocks = nn.ModuleList([])
233
+ self.brushnet_down_blocks = nn.ModuleList([])
234
+
235
+ if isinstance(only_cross_attention, bool):
236
+ only_cross_attention = [only_cross_attention] * len(down_block_types)
237
+
238
+ if isinstance(attention_head_dim, int):
239
+ attention_head_dim = (attention_head_dim,) * len(down_block_types)
240
+
241
+ if isinstance(num_attention_heads, int):
242
+ num_attention_heads = (num_attention_heads,) * len(down_block_types)
243
+
244
+ # down
245
+ output_channel = block_out_channels[0]
246
+
247
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
248
+ brushnet_block = zero_module(brushnet_block)
249
+ self.brushnet_down_blocks.append(brushnet_block)
250
+
251
+ for i, down_block_type in enumerate(down_block_types):
252
+ input_channel = output_channel
253
+ output_channel = block_out_channels[i]
254
+ is_final_block = i == len(block_out_channels) - 1
255
+
256
+ down_block = get_down_block(
257
+ down_block_type,
258
+ num_layers=layers_per_block,
259
+ transformer_layers_per_block=transformer_layers_per_block[i],
260
+ in_channels=input_channel,
261
+ out_channels=output_channel,
262
+ temb_channels=time_embed_dim,
263
+ add_downsample=not is_final_block,
264
+ resnet_eps=norm_eps,
265
+ resnet_act_fn=act_fn,
266
+ resnet_groups=norm_num_groups,
267
+ cross_attention_dim=cross_attention_dim,
268
+ num_attention_heads=num_attention_heads[i],
269
+ attention_head_dim=attention_head_dim[i] if attention_head_dim[i] is not None else output_channel,
270
+ downsample_padding=downsample_padding,
271
+ use_linear_projection=use_linear_projection,
272
+ only_cross_attention=only_cross_attention[i],
273
+ upcast_attention=upcast_attention,
274
+ resnet_time_scale_shift=resnet_time_scale_shift,
275
+ )
276
+ self.down_blocks.append(down_block)
277
+
278
+ for _ in range(layers_per_block):
279
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
280
+ brushnet_block = zero_module(brushnet_block)
281
+ self.brushnet_down_blocks.append(brushnet_block)
282
+
283
+ if not is_final_block:
284
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
285
+ brushnet_block = zero_module(brushnet_block)
286
+ self.brushnet_down_blocks.append(brushnet_block)
287
+
288
+ # mid
289
+ mid_block_channel = block_out_channels[-1]
290
+
291
+ brushnet_block = nn.Conv2d(mid_block_channel, mid_block_channel, kernel_size=1)
292
+ brushnet_block = zero_module(brushnet_block)
293
+ self.brushnet_mid_block = brushnet_block
294
+
295
+ self.mid_block = get_mid_block(
296
+ mid_block_type,
297
+ transformer_layers_per_block=transformer_layers_per_block[-1],
298
+ in_channels=mid_block_channel,
299
+ temb_channels=time_embed_dim,
300
+ resnet_eps=norm_eps,
301
+ resnet_act_fn=act_fn,
302
+ output_scale_factor=mid_block_scale_factor,
303
+ resnet_time_scale_shift=resnet_time_scale_shift,
304
+ cross_attention_dim=cross_attention_dim,
305
+ num_attention_heads=num_attention_heads[-1],
306
+ resnet_groups=norm_num_groups,
307
+ use_linear_projection=use_linear_projection,
308
+ upcast_attention=upcast_attention,
309
+ )
310
+
311
+ # count how many layers upsample the images
312
+ self.num_upsamplers = 0
313
+
314
+ # up
315
+ reversed_block_out_channels = list(reversed(block_out_channels))
316
+ reversed_num_attention_heads = list(reversed(num_attention_heads))
317
+ reversed_transformer_layers_per_block = (list(reversed(transformer_layers_per_block)))
318
+ only_cross_attention = list(reversed(only_cross_attention))
319
+
320
+ output_channel = reversed_block_out_channels[0]
321
+
322
+ self.up_blocks = nn.ModuleList([])
323
+ self.brushnet_up_blocks = nn.ModuleList([])
324
+
325
+ for i, up_block_type in enumerate(up_block_types):
326
+ is_final_block = i == len(block_out_channels) - 1
327
+
328
+ prev_output_channel = output_channel
329
+ output_channel = reversed_block_out_channels[i]
330
+ input_channel = reversed_block_out_channels[min(i + 1, len(block_out_channels) - 1)]
331
+
332
+ # add upsample block for all BUT final layer
333
+ if not is_final_block:
334
+ add_upsample = True
335
+ self.num_upsamplers += 1
336
+ else:
337
+ add_upsample = False
338
+
339
+ up_block = get_up_block(
340
+ up_block_type,
341
+ num_layers=layers_per_block + 1,
342
+ transformer_layers_per_block=reversed_transformer_layers_per_block[i],
343
+ in_channels=input_channel,
344
+ out_channels=output_channel,
345
+ prev_output_channel=prev_output_channel,
346
+ temb_channels=time_embed_dim,
347
+ add_upsample=add_upsample,
348
+ resnet_eps=norm_eps,
349
+ resnet_act_fn=act_fn,
350
+ resolution_idx=i,
351
+ resnet_groups=norm_num_groups,
352
+ cross_attention_dim=cross_attention_dim,
353
+ num_attention_heads=reversed_num_attention_heads[i],
354
+ use_linear_projection=use_linear_projection,
355
+ only_cross_attention=only_cross_attention[i],
356
+ upcast_attention=upcast_attention,
357
+ resnet_time_scale_shift=resnet_time_scale_shift,
358
+ attention_head_dim=attention_head_dim[i] if attention_head_dim[i] is not None else output_channel,
359
+ )
360
+ self.up_blocks.append(up_block)
361
+ prev_output_channel = output_channel
362
+
363
+ for _ in range(layers_per_block + 1):
364
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
365
+ brushnet_block = zero_module(brushnet_block)
366
+ self.brushnet_up_blocks.append(brushnet_block)
367
+
368
+ if not is_final_block:
369
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
370
+ brushnet_block = zero_module(brushnet_block)
371
+ self.brushnet_up_blocks.append(brushnet_block)
372
+
373
+ @classmethod
374
+ def from_unet(
375
+ cls,
376
+ unet: UNet2DConditionModel,
377
+ brushnet_conditioning_channel_order: str = "rgb",
378
+ conditioning_embedding_out_channels: Optional[Tuple[int, ...]] = (16, 32, 96, 256),
379
+ load_weights_from_unet: bool = True,
380
+ conditioning_channels: int = 5,
381
+ ):
382
+ r"""
383
+ Instantiate a [`BrushNetModel`] from [`UNet2DConditionModel`].
384
+
385
+ Parameters:
386
+ unet (`UNet2DConditionModel`):
387
+ The UNet model weights to copy to the [`BrushNetModel`]. All configuration options are also copied
388
+ where applicable.
389
+ """
390
+ transformer_layers_per_block = (
391
+ unet.config.transformer_layers_per_block if "transformer_layers_per_block" in unet.config else 1
392
+ )
393
+ encoder_hid_dim = unet.config.encoder_hid_dim if "encoder_hid_dim" in unet.config else None
394
+ encoder_hid_dim_type = unet.config.encoder_hid_dim_type if "encoder_hid_dim_type" in unet.config else None
395
+ addition_embed_type = unet.config.addition_embed_type if "addition_embed_type" in unet.config else None
396
+ addition_time_embed_dim = (
397
+ unet.config.addition_time_embed_dim if "addition_time_embed_dim" in unet.config else None
398
+ )
399
+
400
+ down_block_types = ["DownBlock2D" for block_name in unet.config.down_block_types]
401
+ mid_block_type = "MidBlock2D"
402
+ up_block_types = ["UpBlock2D" for block_name in unet.config.down_block_types]
403
+
404
+ brushnet = cls(
405
+ in_channels=unet.config.in_channels,
406
+ conditioning_channels=conditioning_channels,
407
+ flip_sin_to_cos=unet.config.flip_sin_to_cos,
408
+ freq_shift=unet.config.freq_shift,
409
+ down_block_types=down_block_types,
410
+ mid_block_type=mid_block_type,
411
+ up_block_types=up_block_types,
412
+ only_cross_attention=unet.config.only_cross_attention,
413
+ block_out_channels=unet.config.block_out_channels,
414
+ layers_per_block=unet.config.layers_per_block,
415
+ downsample_padding=unet.config.downsample_padding,
416
+ mid_block_scale_factor=unet.config.mid_block_scale_factor,
417
+ act_fn=unet.config.act_fn,
418
+ norm_num_groups=unet.config.norm_num_groups,
419
+ norm_eps=unet.config.norm_eps,
420
+ cross_attention_dim=unet.config.cross_attention_dim,
421
+ transformer_layers_per_block=transformer_layers_per_block,
422
+ encoder_hid_dim=encoder_hid_dim,
423
+ encoder_hid_dim_type=encoder_hid_dim_type,
424
+ attention_head_dim=unet.config.attention_head_dim,
425
+ num_attention_heads=unet.config.num_attention_heads,
426
+ use_linear_projection=unet.config.use_linear_projection,
427
+ class_embed_type=unet.config.class_embed_type,
428
+ addition_embed_type=addition_embed_type,
429
+ addition_time_embed_dim=addition_time_embed_dim,
430
+ num_class_embeds=unet.config.num_class_embeds,
431
+ upcast_attention=unet.config.upcast_attention,
432
+ resnet_time_scale_shift=unet.config.resnet_time_scale_shift,
433
+ projection_class_embeddings_input_dim=unet.config.projection_class_embeddings_input_dim,
434
+ brushnet_conditioning_channel_order=brushnet_conditioning_channel_order,
435
+ conditioning_embedding_out_channels=conditioning_embedding_out_channels,
436
+ )
437
+
438
+ if load_weights_from_unet:
439
+ conv_in_condition_weight = torch.zeros_like(brushnet.conv_in_condition.weight)
440
+ conv_in_condition_weight[:, :4, ...] = unet.conv_in.weight
441
+ conv_in_condition_weight[:, 4:8, ...] = unet.conv_in.weight
442
+ brushnet.conv_in_condition.weight = torch.nn.Parameter(conv_in_condition_weight)
443
+ brushnet.conv_in_condition.bias = unet.conv_in.bias
444
+
445
+ brushnet.time_proj.load_state_dict(unet.time_proj.state_dict())
446
+ brushnet.time_embedding.load_state_dict(unet.time_embedding.state_dict())
447
+
448
+ if brushnet.class_embedding:
449
+ brushnet.class_embedding.load_state_dict(unet.class_embedding.state_dict())
450
+
451
+ brushnet.down_blocks.load_state_dict(unet.down_blocks.state_dict(), strict=False)
452
+ brushnet.mid_block.load_state_dict(unet.mid_block.state_dict(), strict=False)
453
+ brushnet.up_blocks.load_state_dict(unet.up_blocks.state_dict(), strict=False)
454
+
455
+ return brushnet
456
+
457
+ @property
458
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.attn_processors
459
+ def attn_processors(self) -> Dict[str, AttentionProcessor]:
460
+ r"""
461
+ Returns:
462
+ `dict` of attention processors: A dictionary containing all attention processors used in the model with
463
+ indexed by its weight name.
464
+ """
465
+ # set recursively
466
+ processors = {}
467
+
468
+ def fn_recursive_add_processors(name: str, module: torch.nn.Module,
469
+ processors: Dict[str, AttentionProcessor]):
470
+ if hasattr(module, "get_processor"):
471
+ processors[f"{name}.processor"] = module.get_processor(return_deprecated_lora=True)
472
+
473
+ for sub_name, child in module.named_children():
474
+ fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
475
+
476
+ return processors
477
+
478
+ for name, module in self.named_children():
479
+ fn_recursive_add_processors(name, module, processors)
480
+
481
+ return processors
482
+
483
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.set_attn_processor
484
+ def set_attn_processor(self, processor: Union[AttentionProcessor, Dict[str, AttentionProcessor]]):
485
+ r"""
486
+ Sets the attention processor to use to compute attention.
487
+
488
+ Parameters:
489
+ processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`):
490
+ The instantiated processor class or a dictionary of processor classes that will be set as the processor
491
+ for **all** `Attention` layers.
492
+
493
+ If `processor` is a dict, the key needs to define the path to the corresponding cross attention
494
+ processor. This is strongly recommended when setting trainable attention processors.
495
+
496
+ """
497
+ count = len(self.attn_processors.keys())
498
+
499
+ if isinstance(processor, dict) and len(processor) != count:
500
+ raise ValueError(
501
+ f"A dict of processors was passed, but the number of processors {len(processor)} does not match the"
502
+ f" number of attention layers: {count}. Please make sure to pass {count} processor classes."
503
+ )
504
+
505
+ def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor):
506
+ if hasattr(module, "set_processor"):
507
+ if not isinstance(processor, dict):
508
+ module.set_processor(processor)
509
+ else:
510
+ module.set_processor(processor.pop(f"{name}.processor"))
511
+
512
+ for sub_name, child in module.named_children():
513
+ fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor)
514
+
515
+ for name, module in self.named_children():
516
+ fn_recursive_attn_processor(name, module, processor)
517
+
518
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.set_default_attn_processor
519
+ def set_default_attn_processor(self):
520
+ """
521
+ Disables custom attention processors and sets the default attention implementation.
522
+ """
523
+ if all(proc.__class__ in ADDED_KV_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
524
+ processor = AttnAddedKVProcessor()
525
+ elif all(proc.__class__ in CROSS_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
526
+ processor = AttnProcessor()
527
+ else:
528
+ raise ValueError(
529
+ f"Cannot call `set_default_attn_processor` when attention processors are of type {next(iter(self.attn_processors.values()))}"
530
+ )
531
+
532
+ self.set_attn_processor(processor)
533
+
534
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.set_attention_slice
535
+ def set_attention_slice(self, slice_size: Union[str, int, List[int]]) -> None:
536
+ r"""
537
+ Enable sliced attention computation.
538
+
539
+ When this option is enabled, the attention module splits the input tensor in slices to compute attention in
540
+ several steps. This is useful for saving some memory in exchange for a small decrease in speed.
541
+
542
+ Args:
543
+ slice_size (`str` or `int` or `list(int)`, *optional*, defaults to `"auto"`):
544
+ When `"auto"`, input to the attention heads is halved, so attention is computed in two steps. If
545
+ `"max"`, maximum amount of memory is saved by running only one slice at a time. If a number is
546
+ provided, uses as many slices as `attention_head_dim // slice_size`. In this case, `attention_head_dim`
547
+ must be a multiple of `slice_size`.
548
+ """
549
+ sliceable_head_dims = []
550
+
551
+ def fn_recursive_retrieve_sliceable_dims(module: torch.nn.Module):
552
+ if hasattr(module, "set_attention_slice"):
553
+ sliceable_head_dims.append(module.sliceable_head_dim)
554
+
555
+ for child in module.children():
556
+ fn_recursive_retrieve_sliceable_dims(child)
557
+
558
+ # retrieve number of attention layers
559
+ for module in self.children():
560
+ fn_recursive_retrieve_sliceable_dims(module)
561
+
562
+ num_sliceable_layers = len(sliceable_head_dims)
563
+
564
+ if slice_size == "auto":
565
+ # half the attention head size is usually a good trade-off between
566
+ # speed and memory
567
+ slice_size = [dim // 2 for dim in sliceable_head_dims]
568
+ elif slice_size == "max":
569
+ # make smallest slice possible
570
+ slice_size = num_sliceable_layers * [1]
571
+
572
+ slice_size = num_sliceable_layers * [slice_size] if not isinstance(slice_size, list) else slice_size
573
+
574
+ if len(slice_size) != len(sliceable_head_dims):
575
+ raise ValueError(
576
+ f"You have provided {len(slice_size)}, but {self.config} has {len(sliceable_head_dims)} different"
577
+ f" attention layers. Make sure to match `len(slice_size)` to be {len(sliceable_head_dims)}."
578
+ )
579
+
580
+ for i in range(len(slice_size)):
581
+ size = slice_size[i]
582
+ dim = sliceable_head_dims[i]
583
+ if size is not None and size > dim:
584
+ raise ValueError(f"size {size} has to be smaller or equal to {dim}.")
585
+
586
+ # Recursively walk through all the children.
587
+ # Any children which exposes the set_attention_slice method
588
+ # gets the message
589
+ def fn_recursive_set_attention_slice(module: torch.nn.Module, slice_size: List[int]):
590
+ if hasattr(module, "set_attention_slice"):
591
+ module.set_attention_slice(slice_size.pop())
592
+
593
+ for child in module.children():
594
+ fn_recursive_set_attention_slice(child, slice_size)
595
+
596
+ reversed_slice_size = list(reversed(slice_size))
597
+ for module in self.children():
598
+ fn_recursive_set_attention_slice(module, reversed_slice_size)
599
+
600
+ def _set_gradient_checkpointing(self, module, value: bool = False) -> None:
601
+ if isinstance(module, (CrossAttnDownBlock2D, DownBlock2D)):
602
+ module.gradient_checkpointing = value
603
+
604
+ def forward(
605
+ self,
606
+ sample: torch.FloatTensor,
607
+ encoder_hidden_states: torch.Tensor,
608
+ brushnet_cond: torch.FloatTensor,
609
+ timestep=None,
610
+ time_emb=None,
611
+ conditioning_scale: float = 1.0,
612
+ class_labels: Optional[torch.Tensor] = None,
613
+ timestep_cond: Optional[torch.Tensor] = None,
614
+ attention_mask: Optional[torch.Tensor] = None,
615
+ added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None,
616
+ cross_attention_kwargs: Optional[Dict[str, Any]] = None,
617
+ guess_mode: bool = False,
618
+ return_dict: bool = True,
619
+ debug=False,
620
+ ) -> Union[BrushNetOutput, Tuple[Tuple[torch.FloatTensor, ...], torch.FloatTensor]]:
621
+
622
+ # check channel order
623
+ channel_order = self.config.brushnet_conditioning_channel_order
624
+
625
+ if channel_order == "rgb":
626
+ # in rgb order by default
627
+ ...
628
+ elif channel_order == "bgr":
629
+ brushnet_cond = torch.flip(brushnet_cond, dims=[1])
630
+ else:
631
+ raise ValueError(f"unknown `brushnet_conditioning_channel_order`: {channel_order}")
632
+
633
+ # prepare attention_mask
634
+ if attention_mask is not None:
635
+ attention_mask = (1 - attention_mask.to(sample.dtype)) * -10000.0
636
+ attention_mask = attention_mask.unsqueeze(1)
637
+
638
+ if timestep is None and time_emb is None:
639
+ raise ValueError(f"`timestep` and `emb` are both None")
640
+
641
+ # print("BN: sample.device", sample.device)
642
+ # print("BN: TE.device", self.time_embedding.linear_1.weight.device)
643
+
644
+ if timestep is not None:
645
+ # 1. time
646
+ timesteps = timestep
647
+ if not torch.is_tensor(timesteps):
648
+ # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can
649
+ # This would be a good case for the `match` statement (Python 3.10+)
650
+ is_mps = sample.device.type == "mps"
651
+ if isinstance(timestep, float):
652
+ dtype = torch.float32 if is_mps else torch.float64
653
+ else:
654
+ dtype = torch.int32 if is_mps else torch.int64
655
+ timesteps = torch.tensor([timesteps], dtype=dtype, device=sample.device)
656
+ elif len(timesteps.shape) == 0:
657
+ timesteps = timesteps[None].to(sample.device)
658
+
659
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
660
+ timesteps = timesteps.expand(sample.shape[0])
661
+
662
+ t_emb = self.time_proj(timesteps)
663
+
664
+ # timesteps does not contain any weights and will always return f32 tensors
665
+ # but time_embedding might actually be running in fp16. so we need to cast here.
666
+ # there might be better ways to encapsulate this.
667
+ t_emb = t_emb.to(dtype=sample.dtype)
668
+
669
+ # print("t_emb.device =",t_emb.device)
670
+
671
+ emb = self.time_embedding(t_emb, timestep_cond)
672
+ aug_emb = None
673
+
674
+ # print('emb.shape', emb.shape)
675
+
676
+ if self.class_embedding is not None:
677
+ if class_labels is None:
678
+ raise ValueError("class_labels should be provided when num_class_embeds > 0")
679
+
680
+ if self.config.class_embed_type == "timestep":
681
+ class_labels = self.time_proj(class_labels)
682
+
683
+ class_emb = self.class_embedding(class_labels).to(dtype=self.dtype)
684
+ emb = emb + class_emb
685
+
686
+ if self.config.addition_embed_type is not None:
687
+ if self.config.addition_embed_type == "text":
688
+ aug_emb = self.add_embedding(encoder_hidden_states)
689
+
690
+ elif self.config.addition_embed_type == "text_time":
691
+ if "text_embeds" not in added_cond_kwargs:
692
+ raise ValueError(
693
+ f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `text_embeds` to be passed in `added_cond_kwargs`"
694
+ )
695
+ text_embeds = added_cond_kwargs.get("text_embeds")
696
+ if "time_ids" not in added_cond_kwargs:
697
+ raise ValueError(
698
+ f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `time_ids` to be passed in `added_cond_kwargs`"
699
+ )
700
+ time_ids = added_cond_kwargs.get("time_ids")
701
+ time_embeds = self.add_time_proj(time_ids.flatten())
702
+ time_embeds = time_embeds.reshape((text_embeds.shape[0], -1))
703
+
704
+ add_embeds = torch.concat([text_embeds, time_embeds], dim=-1)
705
+ add_embeds = add_embeds.to(emb.dtype)
706
+ aug_emb = self.add_embedding(add_embeds)
707
+
708
+ # print('text_embeds', text_embeds.shape, 'time_ids', time_ids.shape, 'time_embeds', time_embeds.shape, 'add__embeds', add_embeds.shape, 'aug_emb', aug_emb.shape)
709
+
710
+ emb = emb + aug_emb if aug_emb is not None else emb
711
+ else:
712
+ emb = time_emb
713
+
714
+ # 2. pre-process
715
+
716
+ brushnet_cond = torch.concat([sample, brushnet_cond], 1)
717
+ sample = self.conv_in_condition(brushnet_cond)
718
+
719
+ # 3. down
720
+ down_block_res_samples = (sample,)
721
+ for downsample_block in self.down_blocks:
722
+ if hasattr(downsample_block, "has_cross_attention") and downsample_block.has_cross_attention:
723
+ sample, res_samples = downsample_block(
724
+ hidden_states=sample,
725
+ temb=emb,
726
+ encoder_hidden_states=encoder_hidden_states,
727
+ attention_mask=attention_mask,
728
+ cross_attention_kwargs=cross_attention_kwargs,
729
+ )
730
+ else:
731
+ sample, res_samples = downsample_block(hidden_states=sample, temb=emb)
732
+
733
+ down_block_res_samples += res_samples
734
+
735
+ # 4. PaintingNet down blocks
736
+ brushnet_down_block_res_samples = ()
737
+ for down_block_res_sample, brushnet_down_block in zip(down_block_res_samples, self.brushnet_down_blocks):
738
+ down_block_res_sample = brushnet_down_block(down_block_res_sample)
739
+ brushnet_down_block_res_samples = brushnet_down_block_res_samples + (down_block_res_sample,)
740
+
741
+ # 5. mid
742
+ if self.mid_block is not None:
743
+ if hasattr(self.mid_block, "has_cross_attention") and self.mid_block.has_cross_attention:
744
+ sample = self.mid_block(
745
+ sample,
746
+ emb,
747
+ encoder_hidden_states=encoder_hidden_states,
748
+ attention_mask=attention_mask,
749
+ cross_attention_kwargs=cross_attention_kwargs,
750
+ )
751
+ else:
752
+ sample = self.mid_block(sample, emb)
753
+
754
+ # 6. BrushNet mid blocks
755
+ brushnet_mid_block_res_sample = self.brushnet_mid_block(sample)
756
+
757
+ # 7. up
758
+ up_block_res_samples = ()
759
+ for i, upsample_block in enumerate(self.up_blocks):
760
+ is_final_block = i == len(self.up_blocks) - 1
761
+
762
+ res_samples = down_block_res_samples[-len(upsample_block.resnets):]
763
+ down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)]
764
+
765
+ # if we have not reached the final block and need to forward the
766
+ # upsample size, we do it here
767
+ if not is_final_block:
768
+ upsample_size = down_block_res_samples[-1].shape[2:]
769
+
770
+ if hasattr(upsample_block, "has_cross_attention") and upsample_block.has_cross_attention:
771
+ sample, up_res_samples = upsample_block(
772
+ hidden_states=sample,
773
+ temb=emb,
774
+ res_hidden_states_tuple=res_samples,
775
+ encoder_hidden_states=encoder_hidden_states,
776
+ cross_attention_kwargs=cross_attention_kwargs,
777
+ upsample_size=upsample_size,
778
+ attention_mask=attention_mask,
779
+ return_res_samples=True
780
+ )
781
+ else:
782
+ sample, up_res_samples = upsample_block(
783
+ hidden_states=sample,
784
+ temb=emb,
785
+ res_hidden_states_tuple=res_samples,
786
+ upsample_size=upsample_size,
787
+ return_res_samples=True
788
+ )
789
+
790
+ up_block_res_samples += up_res_samples
791
+
792
+ # 8. BrushNet up blocks
793
+ brushnet_up_block_res_samples = ()
794
+ for up_block_res_sample, brushnet_up_block in zip(up_block_res_samples, self.brushnet_up_blocks):
795
+ up_block_res_sample = brushnet_up_block(up_block_res_sample)
796
+ brushnet_up_block_res_samples = brushnet_up_block_res_samples + (up_block_res_sample,)
797
+
798
+ # 6. scaling
799
+ if guess_mode and not self.config.global_pool_conditions:
800
+ scales = torch.logspace(-1, 0,
801
+ len(brushnet_down_block_res_samples) + 1 + len(brushnet_up_block_res_samples),
802
+ device=sample.device) # 0.1 to 1.0
803
+ scales = scales * conditioning_scale
804
+
805
+ brushnet_down_block_res_samples = [sample * scale for sample, scale in
806
+ zip(brushnet_down_block_res_samples,
807
+ scales[:len(brushnet_down_block_res_samples)])]
808
+ brushnet_mid_block_res_sample = brushnet_mid_block_res_sample * scales[
809
+ len(brushnet_down_block_res_samples)]
810
+ brushnet_up_block_res_samples = [sample * scale for sample, scale in zip(brushnet_up_block_res_samples,
811
+ scales[
812
+ len(brushnet_down_block_res_samples) + 1:])]
813
+ else:
814
+ brushnet_down_block_res_samples = [sample * conditioning_scale for sample in
815
+ brushnet_down_block_res_samples]
816
+ brushnet_mid_block_res_sample = brushnet_mid_block_res_sample * conditioning_scale
817
+ brushnet_up_block_res_samples = [sample * conditioning_scale for sample in
818
+ brushnet_up_block_res_samples]
819
+
820
+ if self.config.global_pool_conditions:
821
+ brushnet_down_block_res_samples = [
822
+ torch.mean(sample, dim=(2, 3), keepdim=True) for sample in brushnet_down_block_res_samples
823
+ ]
824
+ brushnet_mid_block_res_sample = torch.mean(brushnet_mid_block_res_sample, dim=(2, 3), keepdim=True)
825
+ brushnet_up_block_res_samples = [
826
+ torch.mean(sample, dim=(2, 3), keepdim=True) for sample in brushnet_up_block_res_samples
827
+ ]
828
+
829
+ if not return_dict:
830
+ return (brushnet_down_block_res_samples, brushnet_mid_block_res_sample, brushnet_up_block_res_samples)
831
+
832
+ return BrushNetOutput(
833
+ down_block_res_samples=brushnet_down_block_res_samples,
834
+ mid_block_res_sample=brushnet_mid_block_res_sample,
835
+ up_block_res_samples=brushnet_up_block_res_samples
836
+ )
837
+
838
+ # PowerPaintModel
839
+ class PowerPaintModel(ModelMixin, ConfigMixin):
840
+ _supports_gradient_checkpointing = True
841
+
842
+ @register_to_config
843
+ def __init__(
844
+ self,
845
+ in_channels: int = 4,
846
+ conditioning_channels: int = 5,
847
+ flip_sin_to_cos: bool = True,
848
+ freq_shift: int = 0,
849
+ down_block_types: Tuple[str, ...] = (
850
+ "CrossAttnDownBlock2D",
851
+ "CrossAttnDownBlock2D",
852
+ "CrossAttnDownBlock2D",
853
+ "DownBlock2D",
854
+ ),
855
+ mid_block_type: Optional[str] = "UNetMidBlock2DCrossAttn",
856
+ up_block_types: Tuple[str, ...] = (
857
+ "UpBlock2D",
858
+ "CrossAttnUpBlock2D",
859
+ "CrossAttnUpBlock2D",
860
+ "CrossAttnUpBlock2D",
861
+ ),
862
+ only_cross_attention: Union[bool, Tuple[bool]] = False,
863
+ block_out_channels: Tuple[int, ...] = (320, 640, 1280, 1280),
864
+ layers_per_block: int = 2,
865
+ downsample_padding: int = 1,
866
+ mid_block_scale_factor: float = 1,
867
+ act_fn: str = "silu",
868
+ norm_num_groups: Optional[int] = 32,
869
+ norm_eps: float = 1e-5,
870
+ cross_attention_dim: int = 1280,
871
+ transformer_layers_per_block: Union[int, Tuple[int, ...]] = 1,
872
+ encoder_hid_dim: Optional[int] = None,
873
+ encoder_hid_dim_type: Optional[str] = None,
874
+ attention_head_dim: Union[int, Tuple[int, ...]] = 8,
875
+ num_attention_heads: Optional[Union[int, Tuple[int, ...]]] = None,
876
+ use_linear_projection: bool = False,
877
+ class_embed_type: Optional[str] = None,
878
+ addition_embed_type: Optional[str] = None,
879
+ addition_time_embed_dim: Optional[int] = None,
880
+ num_class_embeds: Optional[int] = None,
881
+ upcast_attention: bool = False,
882
+ resnet_time_scale_shift: str = "default",
883
+ projection_class_embeddings_input_dim: Optional[int] = None,
884
+ brushnet_conditioning_channel_order: str = "rgb",
885
+ conditioning_embedding_out_channels: Optional[Tuple[int, ...]] = (16, 32, 96, 256),
886
+ global_pool_conditions: bool = False,
887
+ addition_embed_type_num_heads: int = 64,
888
+ ):
889
+ super().__init__()
890
+
891
+ # If `num_attention_heads` is not defined (which is the case for most models)
892
+ # it will default to `attention_head_dim`. This looks weird upon first reading it and it is.
893
+ # The reason for this behavior is to correct for incorrectly named variables that were introduced
894
+ # when this library was created. The incorrect naming was only discovered much later in https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131
895
+ # Changing `attention_head_dim` to `num_attention_heads` for 40,000+ configurations is too backwards breaking
896
+ # which is why we correct for the naming here.
897
+ num_attention_heads = num_attention_heads or attention_head_dim
898
+
899
+ # Check inputs
900
+ if len(down_block_types) != len(up_block_types):
901
+ raise ValueError(
902
+ f"Must provide the same number of `down_block_types` as `up_block_types`. `down_block_types`: {down_block_types}. `up_block_types`: {up_block_types}."
903
+ )
904
+
905
+ if len(block_out_channels) != len(down_block_types):
906
+ raise ValueError(
907
+ f"Must provide the same number of `block_out_channels` as `down_block_types`. `block_out_channels`: {block_out_channels}. `down_block_types`: {down_block_types}."
908
+ )
909
+
910
+ if not isinstance(only_cross_attention, bool) and len(only_cross_attention) != len(down_block_types):
911
+ raise ValueError(
912
+ f"Must provide the same number of `only_cross_attention` as `down_block_types`. `only_cross_attention`: {only_cross_attention}. `down_block_types`: {down_block_types}."
913
+ )
914
+
915
+ if not isinstance(num_attention_heads, int) and len(num_attention_heads) != len(down_block_types):
916
+ raise ValueError(
917
+ f"Must provide the same number of `num_attention_heads` as `down_block_types`. `num_attention_heads`: {num_attention_heads}. `down_block_types`: {down_block_types}."
918
+ )
919
+
920
+ if isinstance(transformer_layers_per_block, int):
921
+ transformer_layers_per_block = [transformer_layers_per_block] * len(down_block_types)
922
+
923
+ # input
924
+ conv_in_kernel = 3
925
+ conv_in_padding = (conv_in_kernel - 1) // 2
926
+ self.conv_in_condition = nn.Conv2d(
927
+ in_channels + conditioning_channels,
928
+ block_out_channels[0],
929
+ kernel_size=conv_in_kernel,
930
+ padding=conv_in_padding,
931
+ )
932
+
933
+ # time
934
+ time_embed_dim = block_out_channels[0] * 4
935
+ self.time_proj = Timesteps(block_out_channels[0], flip_sin_to_cos, freq_shift)
936
+ timestep_input_dim = block_out_channels[0]
937
+ self.time_embedding = TimestepEmbedding(
938
+ timestep_input_dim,
939
+ time_embed_dim,
940
+ act_fn=act_fn,
941
+ )
942
+
943
+ if encoder_hid_dim_type is None and encoder_hid_dim is not None:
944
+ encoder_hid_dim_type = "text_proj"
945
+ self.register_to_config(encoder_hid_dim_type=encoder_hid_dim_type)
946
+ logger.info("encoder_hid_dim_type defaults to 'text_proj' as `encoder_hid_dim` is defined.")
947
+
948
+ if encoder_hid_dim is None and encoder_hid_dim_type is not None:
949
+ raise ValueError(
950
+ f"`encoder_hid_dim` has to be defined when `encoder_hid_dim_type` is set to {encoder_hid_dim_type}."
951
+ )
952
+
953
+ if encoder_hid_dim_type == "text_proj":
954
+ self.encoder_hid_proj = nn.Linear(encoder_hid_dim, cross_attention_dim)
955
+ elif encoder_hid_dim_type == "text_image_proj":
956
+ # image_embed_dim DOESN'T have to be `cross_attention_dim`. To not clutter the __init__ too much
957
+ # they are set to `cross_attention_dim` here as this is exactly the required dimension for the currently only use
958
+ # case when `addition_embed_type == "text_image_proj"` (Kadinsky 2.1)`
959
+ self.encoder_hid_proj = TextImageProjection(
960
+ text_embed_dim=encoder_hid_dim,
961
+ image_embed_dim=cross_attention_dim,
962
+ cross_attention_dim=cross_attention_dim,
963
+ )
964
+
965
+ elif encoder_hid_dim_type is not None:
966
+ raise ValueError(
967
+ f"encoder_hid_dim_type: {encoder_hid_dim_type} must be None, 'text_proj' or 'text_image_proj'."
968
+ )
969
+ else:
970
+ self.encoder_hid_proj = None
971
+
972
+ # class embedding
973
+ if class_embed_type is None and num_class_embeds is not None:
974
+ self.class_embedding = nn.Embedding(num_class_embeds, time_embed_dim)
975
+ elif class_embed_type == "timestep":
976
+ self.class_embedding = TimestepEmbedding(timestep_input_dim, time_embed_dim)
977
+ elif class_embed_type == "identity":
978
+ self.class_embedding = nn.Identity(time_embed_dim, time_embed_dim)
979
+ elif class_embed_type == "projection":
980
+ if projection_class_embeddings_input_dim is None:
981
+ raise ValueError(
982
+ "`class_embed_type`: 'projection' requires `projection_class_embeddings_input_dim` be set"
983
+ )
984
+ # The projection `class_embed_type` is the same as the timestep `class_embed_type` except
985
+ # 1. the `class_labels` inputs are not first converted to sinusoidal embeddings
986
+ # 2. it projects from an arbitrary input dimension.
987
+ #
988
+ # Note that `TimestepEmbedding` is quite general, being mainly linear layers and activations.
989
+ # When used for embedding actual timesteps, the timesteps are first converted to sinusoidal embeddings.
990
+ # As a result, `TimestepEmbedding` can be passed arbitrary vectors.
991
+ self.class_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
992
+ else:
993
+ self.class_embedding = None
994
+
995
+ if addition_embed_type == "text":
996
+ if encoder_hid_dim is not None:
997
+ text_time_embedding_from_dim = encoder_hid_dim
998
+ else:
999
+ text_time_embedding_from_dim = cross_attention_dim
1000
+
1001
+ self.add_embedding = TextTimeEmbedding(
1002
+ text_time_embedding_from_dim, time_embed_dim, num_heads=addition_embed_type_num_heads
1003
+ )
1004
+ elif addition_embed_type == "text_image":
1005
+ # text_embed_dim and image_embed_dim DON'T have to be `cross_attention_dim`. To not clutter the __init__ too much
1006
+ # they are set to `cross_attention_dim` here as this is exactly the required dimension for the currently only use
1007
+ # case when `addition_embed_type == "text_image"` (Kadinsky 2.1)`
1008
+ self.add_embedding = TextImageTimeEmbedding(
1009
+ text_embed_dim=cross_attention_dim, image_embed_dim=cross_attention_dim, time_embed_dim=time_embed_dim
1010
+ )
1011
+ elif addition_embed_type == "text_time":
1012
+ self.add_time_proj = Timesteps(addition_time_embed_dim, flip_sin_to_cos, freq_shift)
1013
+ self.add_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
1014
+
1015
+ elif addition_embed_type is not None:
1016
+ raise ValueError(f"addition_embed_type: {addition_embed_type} must be None, 'text' or 'text_image'.")
1017
+
1018
+ self.down_blocks = nn.ModuleList([])
1019
+ self.brushnet_down_blocks = nn.ModuleList([])
1020
+
1021
+ if isinstance(only_cross_attention, bool):
1022
+ only_cross_attention = [only_cross_attention] * len(down_block_types)
1023
+
1024
+ if isinstance(attention_head_dim, int):
1025
+ attention_head_dim = (attention_head_dim,) * len(down_block_types)
1026
+
1027
+ if isinstance(num_attention_heads, int):
1028
+ num_attention_heads = (num_attention_heads,) * len(down_block_types)
1029
+
1030
+ # down
1031
+ output_channel = block_out_channels[0]
1032
+
1033
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
1034
+ brushnet_block = zero_module(brushnet_block)
1035
+ self.brushnet_down_blocks.append(brushnet_block)
1036
+
1037
+ for i, down_block_type in enumerate(down_block_types):
1038
+ input_channel = output_channel
1039
+ output_channel = block_out_channels[i]
1040
+ is_final_block = i == len(block_out_channels) - 1
1041
+
1042
+ down_block = get_down_block(
1043
+ down_block_type,
1044
+ num_layers=layers_per_block,
1045
+ transformer_layers_per_block=transformer_layers_per_block[i],
1046
+ in_channels=input_channel,
1047
+ out_channels=output_channel,
1048
+ temb_channels=time_embed_dim,
1049
+ add_downsample=not is_final_block,
1050
+ resnet_eps=norm_eps,
1051
+ resnet_act_fn=act_fn,
1052
+ resnet_groups=norm_num_groups,
1053
+ cross_attention_dim=cross_attention_dim,
1054
+ num_attention_heads=num_attention_heads[i],
1055
+ attention_head_dim=attention_head_dim[i] if attention_head_dim[i] is not None else output_channel,
1056
+ downsample_padding=downsample_padding,
1057
+ use_linear_projection=use_linear_projection,
1058
+ only_cross_attention=only_cross_attention[i],
1059
+ upcast_attention=upcast_attention,
1060
+ resnet_time_scale_shift=resnet_time_scale_shift,
1061
+ )
1062
+ self.down_blocks.append(down_block)
1063
+
1064
+ for _ in range(layers_per_block):
1065
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
1066
+ brushnet_block = zero_module(brushnet_block)
1067
+ self.brushnet_down_blocks.append(brushnet_block)
1068
+
1069
+ if not is_final_block:
1070
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
1071
+ brushnet_block = zero_module(brushnet_block)
1072
+ self.brushnet_down_blocks.append(brushnet_block)
1073
+
1074
+ # mid
1075
+ mid_block_channel = block_out_channels[-1]
1076
+
1077
+ brushnet_block = nn.Conv2d(mid_block_channel, mid_block_channel, kernel_size=1)
1078
+ brushnet_block = zero_module(brushnet_block)
1079
+ self.brushnet_mid_block = brushnet_block
1080
+
1081
+ self.mid_block = get_mid_block(
1082
+ mid_block_type,
1083
+ transformer_layers_per_block=transformer_layers_per_block[-1],
1084
+ in_channels=mid_block_channel,
1085
+ temb_channels=time_embed_dim,
1086
+ resnet_eps=norm_eps,
1087
+ resnet_act_fn=act_fn,
1088
+ output_scale_factor=mid_block_scale_factor,
1089
+ resnet_time_scale_shift=resnet_time_scale_shift,
1090
+ cross_attention_dim=cross_attention_dim,
1091
+ num_attention_heads=num_attention_heads[-1],
1092
+ resnet_groups=norm_num_groups,
1093
+ use_linear_projection=use_linear_projection,
1094
+ upcast_attention=upcast_attention,
1095
+ )
1096
+
1097
+ # count how many layers upsample the images
1098
+ self.num_upsamplers = 0
1099
+
1100
+ # up
1101
+ reversed_block_out_channels = list(reversed(block_out_channels))
1102
+ reversed_num_attention_heads = list(reversed(num_attention_heads))
1103
+ reversed_transformer_layers_per_block = list(reversed(transformer_layers_per_block))
1104
+ only_cross_attention = list(reversed(only_cross_attention))
1105
+
1106
+ output_channel = reversed_block_out_channels[0]
1107
+
1108
+ self.up_blocks = nn.ModuleList([])
1109
+ self.brushnet_up_blocks = nn.ModuleList([])
1110
+
1111
+ for i, up_block_type in enumerate(up_block_types):
1112
+ is_final_block = i == len(block_out_channels) - 1
1113
+
1114
+ prev_output_channel = output_channel
1115
+ output_channel = reversed_block_out_channels[i]
1116
+ input_channel = reversed_block_out_channels[min(i + 1, len(block_out_channels) - 1)]
1117
+
1118
+ # add upsample block for all BUT final layer
1119
+ if not is_final_block:
1120
+ add_upsample = True
1121
+ self.num_upsamplers += 1
1122
+ else:
1123
+ add_upsample = False
1124
+
1125
+ up_block = get_up_block(
1126
+ up_block_type,
1127
+ num_layers=layers_per_block + 1,
1128
+ transformer_layers_per_block=reversed_transformer_layers_per_block[i],
1129
+ in_channels=input_channel,
1130
+ out_channels=output_channel,
1131
+ prev_output_channel=prev_output_channel,
1132
+ temb_channels=time_embed_dim,
1133
+ add_upsample=add_upsample,
1134
+ resnet_eps=norm_eps,
1135
+ resnet_act_fn=act_fn,
1136
+ resolution_idx=i,
1137
+ resnet_groups=norm_num_groups,
1138
+ cross_attention_dim=cross_attention_dim,
1139
+ num_attention_heads=reversed_num_attention_heads[i],
1140
+ use_linear_projection=use_linear_projection,
1141
+ only_cross_attention=only_cross_attention[i],
1142
+ upcast_attention=upcast_attention,
1143
+ resnet_time_scale_shift=resnet_time_scale_shift,
1144
+ attention_head_dim=attention_head_dim[i] if attention_head_dim[i] is not None else output_channel,
1145
+ )
1146
+ self.up_blocks.append(up_block)
1147
+ prev_output_channel = output_channel
1148
+
1149
+ for _ in range(layers_per_block + 1):
1150
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
1151
+ brushnet_block = zero_module(brushnet_block)
1152
+ self.brushnet_up_blocks.append(brushnet_block)
1153
+
1154
+ if not is_final_block:
1155
+ brushnet_block = nn.Conv2d(output_channel, output_channel, kernel_size=1)
1156
+ brushnet_block = zero_module(brushnet_block)
1157
+ self.brushnet_up_blocks.append(brushnet_block)
1158
+
1159
+ @classmethod
1160
+ def from_unet(
1161
+ cls,
1162
+ unet: UNet2DConditionModel,
1163
+ brushnet_conditioning_channel_order: str = "rgb",
1164
+ conditioning_embedding_out_channels: Optional[Tuple[int, ...]] = (16, 32, 96, 256),
1165
+ load_weights_from_unet: bool = True,
1166
+ conditioning_channels: int = 5,
1167
+ ):
1168
+ r"""
1169
+ Instantiate a [`BrushNetModel`] from [`UNet2DConditionModel`].
1170
+
1171
+ Parameters:
1172
+ unet (`UNet2DConditionModel`):
1173
+ The UNet model weights to copy to the [`BrushNetModel`]. All configuration options are also copied
1174
+ where applicable.
1175
+ """
1176
+ transformer_layers_per_block = (
1177
+ unet.config.transformer_layers_per_block if "transformer_layers_per_block" in unet.config else 1
1178
+ )
1179
+ encoder_hid_dim = unet.config.encoder_hid_dim if "encoder_hid_dim" in unet.config else None
1180
+ encoder_hid_dim_type = unet.config.encoder_hid_dim_type if "encoder_hid_dim_type" in unet.config else None
1181
+ addition_embed_type = unet.config.addition_embed_type if "addition_embed_type" in unet.config else None
1182
+ addition_time_embed_dim = (
1183
+ unet.config.addition_time_embed_dim if "addition_time_embed_dim" in unet.config else None
1184
+ )
1185
+
1186
+ brushnet = cls(
1187
+ in_channels=unet.config.in_channels,
1188
+ conditioning_channels=conditioning_channels,
1189
+ flip_sin_to_cos=unet.config.flip_sin_to_cos,
1190
+ freq_shift=unet.config.freq_shift,
1191
+ # down_block_types=['DownBlock2D','DownBlock2D','DownBlock2D','DownBlock2D'],
1192
+ down_block_types=[
1193
+ "CrossAttnDownBlock2D",
1194
+ "CrossAttnDownBlock2D",
1195
+ "CrossAttnDownBlock2D",
1196
+ "DownBlock2D",
1197
+ ],
1198
+ # mid_block_type='MidBlock2D',
1199
+ mid_block_type="UNetMidBlock2DCrossAttn",
1200
+ # up_block_types=['UpBlock2D','UpBlock2D','UpBlock2D','UpBlock2D'],
1201
+ up_block_types=["UpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D"],
1202
+ only_cross_attention=unet.config.only_cross_attention,
1203
+ block_out_channels=unet.config.block_out_channels,
1204
+ layers_per_block=unet.config.layers_per_block,
1205
+ downsample_padding=unet.config.downsample_padding,
1206
+ mid_block_scale_factor=unet.config.mid_block_scale_factor,
1207
+ act_fn=unet.config.act_fn,
1208
+ norm_num_groups=unet.config.norm_num_groups,
1209
+ norm_eps=unet.config.norm_eps,
1210
+ cross_attention_dim=unet.config.cross_attention_dim,
1211
+ transformer_layers_per_block=transformer_layers_per_block,
1212
+ encoder_hid_dim=encoder_hid_dim,
1213
+ encoder_hid_dim_type=encoder_hid_dim_type,
1214
+ attention_head_dim=unet.config.attention_head_dim,
1215
+ num_attention_heads=unet.config.num_attention_heads,
1216
+ use_linear_projection=unet.config.use_linear_projection,
1217
+ class_embed_type=unet.config.class_embed_type,
1218
+ addition_embed_type=addition_embed_type,
1219
+ addition_time_embed_dim=addition_time_embed_dim,
1220
+ num_class_embeds=unet.config.num_class_embeds,
1221
+ upcast_attention=unet.config.upcast_attention,
1222
+ resnet_time_scale_shift=unet.config.resnet_time_scale_shift,
1223
+ projection_class_embeddings_input_dim=unet.config.projection_class_embeddings_input_dim,
1224
+ brushnet_conditioning_channel_order=brushnet_conditioning_channel_order,
1225
+ conditioning_embedding_out_channels=conditioning_embedding_out_channels,
1226
+ )
1227
+
1228
+ if load_weights_from_unet:
1229
+ conv_in_condition_weight = torch.zeros_like(brushnet.conv_in_condition.weight)
1230
+ conv_in_condition_weight[:, :4, ...] = unet.conv_in.weight
1231
+ conv_in_condition_weight[:, 4:8, ...] = unet.conv_in.weight
1232
+ brushnet.conv_in_condition.weight = torch.nn.Parameter(conv_in_condition_weight)
1233
+ brushnet.conv_in_condition.bias = unet.conv_in.bias
1234
+
1235
+ brushnet.time_proj.load_state_dict(unet.time_proj.state_dict())
1236
+ brushnet.time_embedding.load_state_dict(unet.time_embedding.state_dict())
1237
+
1238
+ if brushnet.class_embedding:
1239
+ brushnet.class_embedding.load_state_dict(unet.class_embedding.state_dict())
1240
+
1241
+ brushnet.down_blocks.load_state_dict(unet.down_blocks.state_dict(), strict=False)
1242
+ brushnet.mid_block.load_state_dict(unet.mid_block.state_dict(), strict=False)
1243
+ brushnet.up_blocks.load_state_dict(unet.up_blocks.state_dict(), strict=False)
1244
+
1245
+ return brushnet.to(unet.dtype)
1246
+
1247
+ @property
1248
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.attn_processors
1249
+ def attn_processors(self) -> Dict[str, AttentionProcessor]:
1250
+ r"""
1251
+ Returns:
1252
+ `dict` of attention processors: A dictionary containing all attention processors used in the model with
1253
+ indexed by its weight name.
1254
+ """
1255
+ # set recursively
1256
+ processors = {}
1257
+
1258
+ def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
1259
+ if hasattr(module, "get_processor"):
1260
+ processors[f"{name}.processor"] = module.get_processor(return_deprecated_lora=True)
1261
+
1262
+ for sub_name, child in module.named_children():
1263
+ fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
1264
+
1265
+ return processors
1266
+
1267
+ for name, module in self.named_children():
1268
+ fn_recursive_add_processors(name, module, processors)
1269
+
1270
+ return processors
1271
+
1272
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.set_attn_processor
1273
+ def set_attn_processor(self, processor: Union[AttentionProcessor, Dict[str, AttentionProcessor]]):
1274
+ r"""
1275
+ Sets the attention processor to use to compute attention.
1276
+
1277
+ Parameters:
1278
+ processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`):
1279
+ The instantiated processor class or a dictionary of processor classes that will be set as the processor
1280
+ for **all** `Attention` layers.
1281
+
1282
+ If `processor` is a dict, the key needs to define the path to the corresponding cross attention
1283
+ processor. This is strongly recommended when setting trainable attention processors.
1284
+
1285
+ """
1286
+ count = len(self.attn_processors.keys())
1287
+
1288
+ if isinstance(processor, dict) and len(processor) != count:
1289
+ raise ValueError(
1290
+ f"A dict of processors was passed, but the number of processors {len(processor)} does not match the"
1291
+ f" number of attention layers: {count}. Please make sure to pass {count} processor classes."
1292
+ )
1293
+
1294
+ def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor):
1295
+ if hasattr(module, "set_processor"):
1296
+ if not isinstance(processor, dict):
1297
+ module.set_processor(processor)
1298
+ else:
1299
+ module.set_processor(processor.pop(f"{name}.processor"))
1300
+
1301
+ for sub_name, child in module.named_children():
1302
+ fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor)
1303
+
1304
+ for name, module in self.named_children():
1305
+ fn_recursive_attn_processor(name, module, processor)
1306
+
1307
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.set_default_attn_processor
1308
+ def set_default_attn_processor(self):
1309
+ """
1310
+ Disables custom attention processors and sets the default attention implementation.
1311
+ """
1312
+ if all(proc.__class__ in ADDED_KV_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
1313
+ processor = AttnAddedKVProcessor()
1314
+ elif all(proc.__class__ in CROSS_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
1315
+ processor = AttnProcessor()
1316
+ else:
1317
+ raise ValueError(
1318
+ f"Cannot call `set_default_attn_processor` when attention processors are of type {next(iter(self.attn_processors.values()))}"
1319
+ )
1320
+
1321
+ self.set_attn_processor(processor)
1322
+
1323
+ # Copied from diffusers.models.unets.unet_2d_condition.UNet2DConditionModel.set_attention_slice
1324
+ def set_attention_slice(self, slice_size: Union[str, int, List[int]]) -> None:
1325
+ r"""
1326
+ Enable sliced attention computation.
1327
+
1328
+ When this option is enabled, the attention module splits the input tensor in slices to compute attention in
1329
+ several steps. This is useful for saving some memory in exchange for a small decrease in speed.
1330
+
1331
+ Args:
1332
+ slice_size (`str` or `int` or `list(int)`, *optional*, defaults to `"auto"`):
1333
+ When `"auto"`, input to the attention heads is halved, so attention is computed in two steps. If
1334
+ `"max"`, maximum amount of memory is saved by running only one slice at a time. If a number is
1335
+ provided, uses as many slices as `attention_head_dim // slice_size`. In this case, `attention_head_dim`
1336
+ must be a multiple of `slice_size`.
1337
+ """
1338
+ sliceable_head_dims = []
1339
+
1340
+ def fn_recursive_retrieve_sliceable_dims(module: torch.nn.Module):
1341
+ if hasattr(module, "set_attention_slice"):
1342
+ sliceable_head_dims.append(module.sliceable_head_dim)
1343
+
1344
+ for child in module.children():
1345
+ fn_recursive_retrieve_sliceable_dims(child)
1346
+
1347
+ # retrieve number of attention layers
1348
+ for module in self.children():
1349
+ fn_recursive_retrieve_sliceable_dims(module)
1350
+
1351
+ num_sliceable_layers = len(sliceable_head_dims)
1352
+
1353
+ if slice_size == "auto":
1354
+ # half the attention head size is usually a good trade-off between
1355
+ # speed and memory
1356
+ slice_size = [dim // 2 for dim in sliceable_head_dims]
1357
+ elif slice_size == "max":
1358
+ # make smallest slice possible
1359
+ slice_size = num_sliceable_layers * [1]
1360
+
1361
+ slice_size = num_sliceable_layers * [slice_size] if not isinstance(slice_size, list) else slice_size
1362
+
1363
+ if len(slice_size) != len(sliceable_head_dims):
1364
+ raise ValueError(
1365
+ f"You have provided {len(slice_size)}, but {self.config} has {len(sliceable_head_dims)} different"
1366
+ f" attention layers. Make sure to match `len(slice_size)` to be {len(sliceable_head_dims)}."
1367
+ )
1368
+
1369
+ for i in range(len(slice_size)):
1370
+ size = slice_size[i]
1371
+ dim = sliceable_head_dims[i]
1372
+ if size is not None and size > dim:
1373
+ raise ValueError(f"size {size} has to be smaller or equal to {dim}.")
1374
+
1375
+ # Recursively walk through all the children.
1376
+ # Any children which exposes the set_attention_slice method
1377
+ # gets the message
1378
+ def fn_recursive_set_attention_slice(module: torch.nn.Module, slice_size: List[int]):
1379
+ if hasattr(module, "set_attention_slice"):
1380
+ module.set_attention_slice(slice_size.pop())
1381
+
1382
+ for child in module.children():
1383
+ fn_recursive_set_attention_slice(child, slice_size)
1384
+
1385
+ reversed_slice_size = list(reversed(slice_size))
1386
+ for module in self.children():
1387
+ fn_recursive_set_attention_slice(module, reversed_slice_size)
1388
+
1389
+ def _set_gradient_checkpointing(self, module, value: bool = False) -> None:
1390
+ if isinstance(module, (CrossAttnDownBlock2D, DownBlock2D)):
1391
+ module.gradient_checkpointing = value
1392
+
1393
+ def forward(
1394
+ self,
1395
+ sample: torch.FloatTensor,
1396
+ timestep: Union[torch.Tensor, float, int],
1397
+ encoder_hidden_states: torch.Tensor,
1398
+ brushnet_cond: torch.FloatTensor,
1399
+ conditioning_scale: float = 1.0,
1400
+ class_labels: Optional[torch.Tensor] = None,
1401
+ timestep_cond: Optional[torch.Tensor] = None,
1402
+ attention_mask: Optional[torch.Tensor] = None,
1403
+ added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None,
1404
+ cross_attention_kwargs: Optional[Dict[str, Any]] = None,
1405
+ guess_mode: bool = False,
1406
+ return_dict: bool = True,
1407
+ debug=False,
1408
+ ) -> Union[BrushNetOutput, Tuple[Tuple[torch.FloatTensor, ...], torch.FloatTensor]]:
1409
+ """
1410
+ The [`BrushNetModel`] forward method.
1411
+
1412
+ Args:
1413
+ sample (`torch.FloatTensor`):
1414
+ The noisy input tensor.
1415
+ timestep (`Union[torch.Tensor, float, int]`):
1416
+ The number of timesteps to denoise an input.
1417
+ encoder_hidden_states (`torch.Tensor`):
1418
+ The encoder hidden states.
1419
+ brushnet_cond (`torch.FloatTensor`):
1420
+ The conditional input tensor of shape `(batch_size, sequence_length, hidden_size)`.
1421
+ conditioning_scale (`float`, defaults to `1.0`):
1422
+ The scale factor for BrushNet outputs.
1423
+ class_labels (`torch.Tensor`, *optional*, defaults to `None`):
1424
+ Optional class labels for conditioning. Their embeddings will be summed with the timestep embeddings.
1425
+ timestep_cond (`torch.Tensor`, *optional*, defaults to `None`):
1426
+ Additional conditional embeddings for timestep. If provided, the embeddings will be summed with the
1427
+ timestep_embedding passed through the `self.time_embedding` layer to obtain the final timestep
1428
+ embeddings.
1429
+ attention_mask (`torch.Tensor`, *optional*, defaults to `None`):
1430
+ An attention mask of shape `(batch, key_tokens)` is applied to `encoder_hidden_states`. If `1` the mask
1431
+ is kept, otherwise if `0` it is discarded. Mask will be converted into a bias, which adds large
1432
+ negative values to the attention scores corresponding to "discard" tokens.
1433
+ added_cond_kwargs (`dict`):
1434
+ Additional conditions for the Stable Diffusion XL UNet.
1435
+ cross_attention_kwargs (`dict[str]`, *optional*, defaults to `None`):
1436
+ A kwargs dictionary that if specified is passed along to the `AttnProcessor`.
1437
+ guess_mode (`bool`, defaults to `False`):
1438
+ In this mode, the BrushNet encoder tries its best to recognize the input content of the input even if
1439
+ you remove all prompts. A `guidance_scale` between 3.0 and 5.0 is recommended.
1440
+ return_dict (`bool`, defaults to `True`):
1441
+ Whether or not to return a [`~models.brushnet.BrushNetOutput`] instead of a plain tuple.
1442
+
1443
+ Returns:
1444
+ [`~models.brushnet.BrushNetOutput`] **or** `tuple`:
1445
+ If `return_dict` is `True`, a [`~models.brushnet.BrushNetOutput`] is returned, otherwise a tuple is
1446
+ returned where the first element is the sample tensor.
1447
+ """
1448
+ # check channel order
1449
+ channel_order = self.config.brushnet_conditioning_channel_order
1450
+
1451
+ if channel_order == "rgb":
1452
+ # in rgb order by default
1453
+ ...
1454
+ elif channel_order == "bgr":
1455
+ brushnet_cond = torch.flip(brushnet_cond, dims=[1])
1456
+ else:
1457
+ raise ValueError(f"unknown `brushnet_conditioning_channel_order`: {channel_order}")
1458
+
1459
+ if debug: print('BrushNet CA: attn mask')
1460
+
1461
+ # prepare attention_mask
1462
+ if attention_mask is not None:
1463
+ attention_mask = (1 - attention_mask.to(sample.dtype)) * -10000.0
1464
+ attention_mask = attention_mask.unsqueeze(1)
1465
+
1466
+ if debug: print('BrushNet CA: time')
1467
+
1468
+ # 1. time
1469
+ timesteps = timestep
1470
+ if not torch.is_tensor(timesteps):
1471
+ # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can
1472
+ # This would be a good case for the `match` statement (Python 3.10+)
1473
+ is_mps = sample.device.type == "mps"
1474
+ if isinstance(timestep, float):
1475
+ dtype = torch.float32 if is_mps else torch.float64
1476
+ else:
1477
+ dtype = torch.int32 if is_mps else torch.int64
1478
+ timesteps = torch.tensor([timesteps], dtype=dtype, device=sample.device)
1479
+ elif len(timesteps.shape) == 0:
1480
+ timesteps = timesteps[None].to(sample.device)
1481
+
1482
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
1483
+ timesteps = timesteps.expand(sample.shape[0])
1484
+
1485
+ t_emb = self.time_proj(timesteps)
1486
+
1487
+ # timesteps does not contain any weights and will always return f32 tensors
1488
+ # but time_embedding might actually be running in fp16. so we need to cast here.
1489
+ # there might be better ways to encapsulate this.
1490
+ t_emb = t_emb.to(dtype=sample.dtype)
1491
+
1492
+ emb = self.time_embedding(t_emb, timestep_cond)
1493
+ aug_emb = None
1494
+
1495
+ if self.class_embedding is not None:
1496
+ if class_labels is None:
1497
+ raise ValueError("class_labels should be provided when num_class_embeds > 0")
1498
+
1499
+ if self.config.class_embed_type == "timestep":
1500
+ class_labels = self.time_proj(class_labels)
1501
+
1502
+ class_emb = self.class_embedding(class_labels).to(dtype=self.dtype)
1503
+ emb = emb + class_emb
1504
+
1505
+ if self.config.addition_embed_type is not None:
1506
+ if self.config.addition_embed_type == "text":
1507
+ aug_emb = self.add_embedding(encoder_hidden_states)
1508
+
1509
+ elif self.config.addition_embed_type == "text_time":
1510
+ if "text_embeds" not in added_cond_kwargs:
1511
+ raise ValueError(
1512
+ f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `text_embeds` to be passed in `added_cond_kwargs`"
1513
+ )
1514
+ text_embeds = added_cond_kwargs.get("text_embeds")
1515
+ if "time_ids" not in added_cond_kwargs:
1516
+ raise ValueError(
1517
+ f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `time_ids` to be passed in `added_cond_kwargs`"
1518
+ )
1519
+ time_ids = added_cond_kwargs.get("time_ids")
1520
+ time_embeds = self.add_time_proj(time_ids.flatten())
1521
+ time_embeds = time_embeds.reshape((text_embeds.shape[0], -1))
1522
+
1523
+ add_embeds = torch.concat([text_embeds, time_embeds], dim=-1)
1524
+ add_embeds = add_embeds.to(emb.dtype)
1525
+ aug_emb = self.add_embedding(add_embeds)
1526
+
1527
+ emb = emb + aug_emb if aug_emb is not None else emb
1528
+
1529
+ if debug: print('BrushNet CA: pre-process')
1530
+
1531
+
1532
+ # 2. pre-process
1533
+ brushnet_cond = torch.concat([sample, brushnet_cond], 1)
1534
+ sample = self.conv_in_condition(brushnet_cond)
1535
+
1536
+ if debug: print('BrushNet CA: down')
1537
+
1538
+ # 3. down
1539
+ down_block_res_samples = (sample,)
1540
+ for downsample_block in self.down_blocks:
1541
+ if hasattr(downsample_block, "has_cross_attention") and downsample_block.has_cross_attention:
1542
+ if debug: print('BrushNet CA (down block with XA): ', type(downsample_block))
1543
+ sample, res_samples = downsample_block(
1544
+ hidden_states=sample,
1545
+ temb=emb,
1546
+ encoder_hidden_states=encoder_hidden_states,
1547
+ attention_mask=attention_mask,
1548
+ cross_attention_kwargs=cross_attention_kwargs,
1549
+ debug=debug,
1550
+ )
1551
+ else:
1552
+ if debug: print('BrushNet CA (down block): ', type(downsample_block))
1553
+ sample, res_samples = downsample_block(hidden_states=sample, temb=emb, debug=debug)
1554
+
1555
+ down_block_res_samples += res_samples
1556
+
1557
+ if debug: print('BrushNet CA: PP down')
1558
+
1559
+ # 4. PaintingNet down blocks
1560
+ brushnet_down_block_res_samples = ()
1561
+ for down_block_res_sample, brushnet_down_block in zip(down_block_res_samples, self.brushnet_down_blocks):
1562
+ down_block_res_sample = brushnet_down_block(down_block_res_sample)
1563
+ brushnet_down_block_res_samples = brushnet_down_block_res_samples + (down_block_res_sample,)
1564
+
1565
+ if debug: print('BrushNet CA: PP mid')
1566
+
1567
+ # 5. mid
1568
+ if self.mid_block is not None:
1569
+ if hasattr(self.mid_block, "has_cross_attention") and self.mid_block.has_cross_attention:
1570
+ sample = self.mid_block(
1571
+ sample,
1572
+ emb,
1573
+ encoder_hidden_states=encoder_hidden_states,
1574
+ attention_mask=attention_mask,
1575
+ cross_attention_kwargs=cross_attention_kwargs,
1576
+ )
1577
+ else:
1578
+ sample = self.mid_block(sample, emb)
1579
+
1580
+ if debug: print('BrushNet CA: mid')
1581
+
1582
+ # 6. BrushNet mid blocks
1583
+ brushnet_mid_block_res_sample = self.brushnet_mid_block(sample)
1584
+
1585
+ if debug: print('BrushNet CA: PP up')
1586
+
1587
+ # 7. up
1588
+ up_block_res_samples = ()
1589
+ for i, upsample_block in enumerate(self.up_blocks):
1590
+ is_final_block = i == len(self.up_blocks) - 1
1591
+
1592
+ res_samples = down_block_res_samples[-len(upsample_block.resnets) :]
1593
+ down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)]
1594
+
1595
+ # if we have not reached the final block and need to forward the
1596
+ # upsample size, we do it here
1597
+ if not is_final_block:
1598
+ upsample_size = down_block_res_samples[-1].shape[2:]
1599
+
1600
+ if hasattr(upsample_block, "has_cross_attention") and upsample_block.has_cross_attention:
1601
+ sample, up_res_samples = upsample_block(
1602
+ hidden_states=sample,
1603
+ temb=emb,
1604
+ res_hidden_states_tuple=res_samples,
1605
+ encoder_hidden_states=encoder_hidden_states,
1606
+ cross_attention_kwargs=cross_attention_kwargs,
1607
+ upsample_size=upsample_size,
1608
+ attention_mask=attention_mask,
1609
+ return_res_samples=True,
1610
+ )
1611
+ else:
1612
+ sample, up_res_samples = upsample_block(
1613
+ hidden_states=sample,
1614
+ temb=emb,
1615
+ res_hidden_states_tuple=res_samples,
1616
+ upsample_size=upsample_size,
1617
+ return_res_samples=True,
1618
+ )
1619
+
1620
+ up_block_res_samples += up_res_samples
1621
+
1622
+ if debug: print('BrushNet CA: up')
1623
+
1624
+ # 8. BrushNet up blocks
1625
+ brushnet_up_block_res_samples = ()
1626
+ for up_block_res_sample, brushnet_up_block in zip(up_block_res_samples, self.brushnet_up_blocks):
1627
+ up_block_res_sample = brushnet_up_block(up_block_res_sample)
1628
+ brushnet_up_block_res_samples = brushnet_up_block_res_samples + (up_block_res_sample,)
1629
+
1630
+ if debug: print('BrushNet CA: scaling')
1631
+
1632
+ # 6. scaling
1633
+ if guess_mode and not self.config.global_pool_conditions:
1634
+ scales = torch.logspace(
1635
+ -1,
1636
+ 0,
1637
+ len(brushnet_down_block_res_samples) + 1 + len(brushnet_up_block_res_samples),
1638
+ device=sample.device,
1639
+ ) # 0.1 to 1.0
1640
+ scales = scales * conditioning_scale
1641
+
1642
+ brushnet_down_block_res_samples = [
1643
+ sample * scale
1644
+ for sample, scale in zip(
1645
+ brushnet_down_block_res_samples, scales[: len(brushnet_down_block_res_samples)]
1646
+ )
1647
+ ]
1648
+ brushnet_mid_block_res_sample = (
1649
+ brushnet_mid_block_res_sample * scales[len(brushnet_down_block_res_samples)]
1650
+ )
1651
+ brushnet_up_block_res_samples = [
1652
+ sample * scale
1653
+ for sample, scale in zip(
1654
+ brushnet_up_block_res_samples, scales[len(brushnet_down_block_res_samples) + 1 :]
1655
+ )
1656
+ ]
1657
+ else:
1658
+ brushnet_down_block_res_samples = [
1659
+ sample * conditioning_scale for sample in brushnet_down_block_res_samples
1660
+ ]
1661
+ brushnet_mid_block_res_sample = brushnet_mid_block_res_sample * conditioning_scale
1662
+ brushnet_up_block_res_samples = [sample * conditioning_scale for sample in brushnet_up_block_res_samples]
1663
+
1664
+ if self.config.global_pool_conditions:
1665
+ brushnet_down_block_res_samples = [
1666
+ torch.mean(sample, dim=(2, 3), keepdim=True) for sample in brushnet_down_block_res_samples
1667
+ ]
1668
+ brushnet_mid_block_res_sample = torch.mean(brushnet_mid_block_res_sample, dim=(2, 3), keepdim=True)
1669
+ brushnet_up_block_res_samples = [
1670
+ torch.mean(sample, dim=(2, 3), keepdim=True) for sample in brushnet_up_block_res_samples
1671
+ ]
1672
+
1673
+ if debug: print('BrushNet CA: finish')
1674
+
1675
+ if not return_dict:
1676
+ return (brushnet_down_block_res_samples, brushnet_mid_block_res_sample, brushnet_up_block_res_samples)
1677
+
1678
+ return BrushNetOutput(
1679
+ down_block_res_samples=brushnet_down_block_res_samples,
1680
+ mid_block_res_sample=brushnet_mid_block_res_sample,
1681
+ up_block_res_samples=brushnet_up_block_res_samples,
1682
+ )
1683
+
1684
+ except ImportError:
1685
+ BrushNetModel = None
1686
+ PowerPaintModel = None
1687
+ # print("\33[33mModule 'diffusers' load failed. If you don't have it installed, do it:\033[0m")
1688
+ # print("\33[33mpip install diffusers\033[0m")
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/model_patch.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import comfy
3
+
4
+ # Check and add 'model_patch' to model.model_options['transformer_options']
5
+ def add_model_patch_option(model):
6
+ if 'transformer_options' not in model.model_options:
7
+ model.model_options['transformer_options'] = {}
8
+ to = model.model_options['transformer_options']
9
+ if "model_patch" not in to:
10
+ to["model_patch"] = {}
11
+ return to
12
+
13
+
14
+ # Patch model with model_function_wrapper
15
+ def patch_model_function_wrapper(model, forward_patch, remove=False):
16
+ def brushnet_model_function_wrapper(apply_model_method, options_dict):
17
+ to = options_dict['c']['transformer_options']
18
+
19
+ control = None
20
+ if 'control' in options_dict['c']:
21
+ control = options_dict['c']['control']
22
+
23
+ x = options_dict['input']
24
+ timestep = options_dict['timestep']
25
+
26
+ # check if there are patches to execute
27
+ if 'model_patch' not in to or 'forward' not in to['model_patch']:
28
+ return apply_model_method(x, timestep, **options_dict['c'])
29
+
30
+ mp = to['model_patch']
31
+ unet = mp['unet']
32
+
33
+ all_sigmas = mp['all_sigmas']
34
+ sigma = to['sigmas'][0].item()
35
+ total_steps = all_sigmas.shape[0] - 1
36
+ step = torch.argmin((all_sigmas - sigma).abs()).item()
37
+
38
+ mp['step'] = step
39
+ mp['total_steps'] = total_steps
40
+
41
+ # comfy.model_base.apply_model
42
+ xc = model.model.model_sampling.calculate_input(timestep, x)
43
+ if 'c_concat' in options_dict['c'] and options_dict['c']['c_concat'] is not None:
44
+ xc = torch.cat([xc] + [options_dict['c']['c_concat']], dim=1)
45
+ t = model.model.model_sampling.timestep(timestep).float()
46
+ # execute all patches
47
+ for method in mp['forward']:
48
+ method(unet, xc, t, to, control)
49
+
50
+ return apply_model_method(x, timestep, **options_dict['c'])
51
+
52
+ if "model_function_wrapper" in model.model_options and model.model_options["model_function_wrapper"]:
53
+ print('BrushNet is going to replace existing model_function_wrapper:',
54
+ model.model_options["model_function_wrapper"])
55
+ model.set_model_unet_function_wrapper(brushnet_model_function_wrapper)
56
+
57
+ to = add_model_patch_option(model)
58
+ mp = to['model_patch']
59
+
60
+ if isinstance(model.model.model_config, comfy.supported_models.SD15):
61
+ mp['SDXL'] = False
62
+ elif isinstance(model.model.model_config, comfy.supported_models.SDXL):
63
+ mp['SDXL'] = True
64
+ else:
65
+ print('Base model type: ', type(model.model.model_config))
66
+ raise Exception("Unsupported model type: ", type(model.model.model_config))
67
+
68
+ if 'forward' not in mp:
69
+ mp['forward'] = []
70
+
71
+ if remove:
72
+ if forward_patch in mp['forward']:
73
+ mp['forward'].remove(forward_patch)
74
+ else:
75
+ mp['forward'].append(forward_patch)
76
+
77
+ mp['unet'] = model.model.diffusion_model
78
+ mp['step'] = 0
79
+ mp['total_steps'] = 1
80
+
81
+ # apply patches to code
82
+ if comfy.samplers.sample.__doc__ is None or 'BrushNet' not in comfy.samplers.sample.__doc__:
83
+ comfy.samplers.original_sample = comfy.samplers.sample
84
+ comfy.samplers.sample = modified_sample
85
+
86
+ if comfy.ldm.modules.diffusionmodules.openaimodel.apply_control.__doc__ is None or \
87
+ 'BrushNet' not in comfy.ldm.modules.diffusionmodules.openaimodel.apply_control.__doc__:
88
+ comfy.ldm.modules.diffusionmodules.openaimodel.original_apply_control = comfy.ldm.modules.diffusionmodules.openaimodel.apply_control
89
+ comfy.ldm.modules.diffusionmodules.openaimodel.apply_control = modified_apply_control
90
+
91
+
92
+ # Model needs current step number and cfg at inference step. It is possible to write a custom KSampler but I'd like to use ComfyUI's one.
93
+ # The first versions had modified_common_ksampler, but it broke custom KSampler nodes
94
+ def modified_sample(model, noise, positive, negative, cfg, device, sampler, sigmas, model_options={},
95
+ latent_image=None, denoise_mask=None, callback=None, disable_pbar=False, seed=None):
96
+ ''' Modified by BrushNet nodes'''
97
+ cfg_guider = comfy.samplers.CFGGuider(model)
98
+ cfg_guider.set_conds(positive, negative)
99
+ cfg_guider.set_cfg(cfg)
100
+
101
+ ### Modified part ######################################################################
102
+ to = add_model_patch_option(model)
103
+ to['model_patch']['all_sigmas'] = sigmas
104
+ #######################################################################################
105
+
106
+ return cfg_guider.sample(noise, latent_image, sampler, sigmas, denoise_mask, callback, disable_pbar, seed)
107
+
108
+ # To use Controlnet with RAUNet it is much easier to modify apply_control a little
109
+ def modified_apply_control(h, control, name):
110
+ '''Modified by BrushNet nodes'''
111
+ if control is not None and name in control and len(control[name]) > 0:
112
+ ctrl = control[name].pop()
113
+ if ctrl is not None:
114
+ if h.shape[2] != ctrl.shape[2] or h.shape[3] != ctrl.shape[3]:
115
+ ctrl = torch.nn.functional.interpolate(ctrl, size=(h.shape[2], h.shape[3]), mode='bicubic').to(
116
+ h.dtype).to(h.device)
117
+ try:
118
+ h += ctrl
119
+ except:
120
+ print.warning("warning control could not be applied {} {}".format(h.shape, ctrl.shape))
121
+ return h
122
+
123
+ def add_model_patch(model):
124
+ to = add_model_patch_option(model)
125
+ mp = to['model_patch']
126
+ if "brushnet" in mp:
127
+ if isinstance(model.model.model_config, comfy.supported_models.SD15):
128
+ mp['SDXL'] = False
129
+ elif isinstance(model.model.model_config, comfy.supported_models.SDXL):
130
+ mp['SDXL'] = True
131
+ else:
132
+ print('Base model type: ', type(model.model.model_config))
133
+ raise Exception("Unsupported model type: ", type(model.model.model_config))
134
+
135
+ mp['unet'] = model.model.diffusion_model
136
+ mp['step'] = 0
137
+ mp['total_steps'] = 1
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/powerpaint_utils.py ADDED
@@ -0,0 +1,467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import random
3
+
4
+ import torch
5
+ import torch.nn as nn
6
+ from transformers import CLIPTokenizer
7
+ from typing import Any, List, Optional, Union
8
+
9
+
10
+ class TokenizerWrapper:
11
+ """Tokenizer wrapper for CLIPTokenizer. Only support CLIPTokenizer
12
+ currently. This wrapper is modified from https://github.com/huggingface/dif
13
+ fusers/blob/e51f19aee82c8dd874b715a09dbc521d88835d68/src/diffusers/loaders.
14
+ py#L358 # noqa.
15
+
16
+ Args:
17
+ from_pretrained (Union[str, os.PathLike], optional): The *model id*
18
+ of a pretrained model or a path to a *directory* containing
19
+ model weights and config. Defaults to None.
20
+ from_config (Union[str, os.PathLike], optional): The *model id*
21
+ of a pretrained model or a path to a *directory* containing
22
+ model weights and config. Defaults to None.
23
+
24
+ *args, **kwargs: If `from_pretrained` is passed, *args and **kwargs
25
+ will be passed to `from_pretrained` function. Otherwise, *args
26
+ and **kwargs will be used to initialize the model by
27
+ `self._module_cls(*args, **kwargs)`.
28
+ """
29
+
30
+ def __init__(self, tokenizer: CLIPTokenizer):
31
+ self.wrapped = tokenizer
32
+ self.token_map = {}
33
+
34
+ def __getattr__(self, name: str) -> Any:
35
+ if name in self.__dict__:
36
+ return getattr(self, name)
37
+ # if name == "wrapped":
38
+ # return getattr(self, 'wrapped')#super().__getattr__("wrapped")
39
+
40
+ try:
41
+ return getattr(self.wrapped, name)
42
+ except AttributeError:
43
+ raise AttributeError(
44
+ "'name' cannot be found in both "
45
+ f"'{self.__class__.__name__}' and "
46
+ f"'{self.__class__.__name__}.tokenizer'."
47
+ )
48
+
49
+ def try_adding_tokens(self, tokens: Union[str, List[str]], *args, **kwargs):
50
+ """Attempt to add tokens to the tokenizer.
51
+
52
+ Args:
53
+ tokens (Union[str, List[str]]): The tokens to be added.
54
+ """
55
+ num_added_tokens = self.wrapped.add_tokens(tokens, *args, **kwargs)
56
+ assert num_added_tokens != 0, (
57
+ f"The tokenizer already contains the token {tokens}. Please pass "
58
+ "a different `placeholder_token` that is not already in the "
59
+ "tokenizer."
60
+ )
61
+
62
+ def get_token_info(self, token: str) -> dict:
63
+ """Get the information of a token, including its start and end index in
64
+ the current tokenizer.
65
+
66
+ Args:
67
+ token (str): The token to be queried.
68
+
69
+ Returns:
70
+ dict: The information of the token, including its start and end
71
+ index in current tokenizer.
72
+ """
73
+ token_ids = self.__call__(token).input_ids
74
+ start, end = token_ids[1], token_ids[-2] + 1
75
+ return {"name": token, "start": start, "end": end}
76
+
77
+ def add_placeholder_token(self, placeholder_token: str, *args, num_vec_per_token: int = 1, **kwargs):
78
+ """Add placeholder tokens to the tokenizer.
79
+
80
+ Args:
81
+ placeholder_token (str): The placeholder token to be added.
82
+ num_vec_per_token (int, optional): The number of vectors of
83
+ the added placeholder token.
84
+ *args, **kwargs: The arguments for `self.wrapped.add_tokens`.
85
+ """
86
+ output = []
87
+ if num_vec_per_token == 1:
88
+ self.try_adding_tokens(placeholder_token, *args, **kwargs)
89
+ output.append(placeholder_token)
90
+ else:
91
+ output = []
92
+ for i in range(num_vec_per_token):
93
+ ith_token = placeholder_token + f"_{i}"
94
+ self.try_adding_tokens(ith_token, *args, **kwargs)
95
+ output.append(ith_token)
96
+
97
+ for token in self.token_map:
98
+ if token in placeholder_token:
99
+ raise ValueError(
100
+ f"The tokenizer already has placeholder token {token} "
101
+ f"that can get confused with {placeholder_token} "
102
+ "keep placeholder tokens independent"
103
+ )
104
+ self.token_map[placeholder_token] = output
105
+
106
+ def replace_placeholder_tokens_in_text(
107
+ self, text: Union[str, List[str]], vector_shuffle: bool = False, prop_tokens_to_load: float = 1.0
108
+ ) -> Union[str, List[str]]:
109
+ """Replace the keywords in text with placeholder tokens. This function
110
+ will be called in `self.__call__` and `self.encode`.
111
+
112
+ Args:
113
+ text (Union[str, List[str]]): The text to be processed.
114
+ vector_shuffle (bool, optional): Whether to shuffle the vectors.
115
+ Defaults to False.
116
+ prop_tokens_to_load (float, optional): The proportion of tokens to
117
+ be loaded. If 1.0, all tokens will be loaded. Defaults to 1.0.
118
+
119
+ Returns:
120
+ Union[str, List[str]]: The processed text.
121
+ """
122
+ if isinstance(text, list):
123
+ output = []
124
+ for i in range(len(text)):
125
+ output.append(self.replace_placeholder_tokens_in_text(text[i], vector_shuffle=vector_shuffle))
126
+ return output
127
+
128
+ for placeholder_token in self.token_map:
129
+ if placeholder_token in text:
130
+ tokens = self.token_map[placeholder_token]
131
+ tokens = tokens[: 1 + int(len(tokens) * prop_tokens_to_load)]
132
+ if vector_shuffle:
133
+ tokens = copy.copy(tokens)
134
+ random.shuffle(tokens)
135
+ text = text.replace(placeholder_token, " ".join(tokens))
136
+ return text
137
+
138
+ def replace_text_with_placeholder_tokens(self, text: Union[str, List[str]]) -> Union[str, List[str]]:
139
+ """Replace the placeholder tokens in text with the original keywords.
140
+ This function will be called in `self.decode`.
141
+
142
+ Args:
143
+ text (Union[str, List[str]]): The text to be processed.
144
+
145
+ Returns:
146
+ Union[str, List[str]]: The processed text.
147
+ """
148
+ if isinstance(text, list):
149
+ output = []
150
+ for i in range(len(text)):
151
+ output.append(self.replace_text_with_placeholder_tokens(text[i]))
152
+ return output
153
+
154
+ for placeholder_token, tokens in self.token_map.items():
155
+ merged_tokens = " ".join(tokens)
156
+ if merged_tokens in text:
157
+ text = text.replace(merged_tokens, placeholder_token)
158
+ return text
159
+
160
+ def __call__(
161
+ self,
162
+ text: Union[str, List[str]],
163
+ *args,
164
+ vector_shuffle: bool = False,
165
+ prop_tokens_to_load: float = 1.0,
166
+ **kwargs,
167
+ ):
168
+ """The call function of the wrapper.
169
+
170
+ Args:
171
+ text (Union[str, List[str]]): The text to be tokenized.
172
+ vector_shuffle (bool, optional): Whether to shuffle the vectors.
173
+ Defaults to False.
174
+ prop_tokens_to_load (float, optional): The proportion of tokens to
175
+ be loaded. If 1.0, all tokens will be loaded. Defaults to 1.0
176
+ *args, **kwargs: The arguments for `self.wrapped.__call__`.
177
+ """
178
+ replaced_text = self.replace_placeholder_tokens_in_text(
179
+ text, vector_shuffle=vector_shuffle, prop_tokens_to_load=prop_tokens_to_load
180
+ )
181
+
182
+ return self.wrapped.__call__(replaced_text, *args, **kwargs)
183
+
184
+ def encode(self, text: Union[str, List[str]], *args, **kwargs):
185
+ """Encode the passed text to token index.
186
+
187
+ Args:
188
+ text (Union[str, List[str]]): The text to be encode.
189
+ *args, **kwargs: The arguments for `self.wrapped.__call__`.
190
+ """
191
+ replaced_text = self.replace_placeholder_tokens_in_text(text)
192
+ return self.wrapped(replaced_text, *args, **kwargs)
193
+
194
+ def decode(self, token_ids, return_raw: bool = False, *args, **kwargs) -> Union[str, List[str]]:
195
+ """Decode the token index to text.
196
+
197
+ Args:
198
+ token_ids: The token index to be decoded.
199
+ return_raw: Whether keep the placeholder token in the text.
200
+ Defaults to False.
201
+ *args, **kwargs: The arguments for `self.wrapped.decode`.
202
+
203
+ Returns:
204
+ Union[str, List[str]]: The decoded text.
205
+ """
206
+ text = self.wrapped.decode(token_ids, *args, **kwargs)
207
+ if return_raw:
208
+ return text
209
+ replaced_text = self.replace_text_with_placeholder_tokens(text)
210
+ return replaced_text
211
+
212
+ def __repr__(self):
213
+ """The representation of the wrapper."""
214
+ s = super().__repr__()
215
+ prefix = f"Wrapped Module Class: {self._module_cls}\n"
216
+ prefix += f"Wrapped Module Name: {self._module_name}\n"
217
+ if self._from_pretrained:
218
+ prefix += f"From Pretrained: {self._from_pretrained}\n"
219
+ s = prefix + s
220
+ return s
221
+
222
+
223
+ class EmbeddingLayerWithFixes(nn.Module):
224
+ """The revised embedding layer to support external embeddings. This design
225
+ of this class is inspired by https://github.com/AUTOMATIC1111/stable-
226
+ diffusion-webui/blob/22bcc7be428c94e9408f589966c2040187245d81/modules/sd_hi
227
+ jack.py#L224 # noqa.
228
+
229
+ Args:
230
+ wrapped (nn.Emebdding): The embedding layer to be wrapped.
231
+ external_embeddings (Union[dict, List[dict]], optional): The external
232
+ embeddings added to this layer. Defaults to None.
233
+ """
234
+
235
+ def __init__(self, wrapped: nn.Embedding, external_embeddings: Optional[Union[dict, List[dict]]] = None):
236
+ super().__init__()
237
+ self.wrapped = wrapped
238
+ self.num_embeddings = wrapped.weight.shape[0]
239
+
240
+ self.external_embeddings = []
241
+ if external_embeddings:
242
+ self.add_embeddings(external_embeddings)
243
+
244
+ self.trainable_embeddings = nn.ParameterDict()
245
+
246
+ @property
247
+ def weight(self):
248
+ """Get the weight of wrapped embedding layer."""
249
+ return self.wrapped.weight
250
+
251
+ def check_duplicate_names(self, embeddings: List[dict]):
252
+ """Check whether duplicate names exist in list of 'external
253
+ embeddings'.
254
+
255
+ Args:
256
+ embeddings (List[dict]): A list of embedding to be check.
257
+ """
258
+ names = [emb["name"] for emb in embeddings]
259
+ assert len(names) == len(set(names)), (
260
+ "Found duplicated names in 'external_embeddings'. Name list: " f"'{names}'"
261
+ )
262
+
263
+ def check_ids_overlap(self, embeddings):
264
+ """Check whether overlap exist in token ids of 'external_embeddings'.
265
+
266
+ Args:
267
+ embeddings (List[dict]): A list of embedding to be check.
268
+ """
269
+ ids_range = [[emb["start"], emb["end"], emb["name"]] for emb in embeddings]
270
+ ids_range.sort() # sort by 'start'
271
+ # check if 'end' has overlapping
272
+ for idx in range(len(ids_range) - 1):
273
+ name1, name2 = ids_range[idx][-1], ids_range[idx + 1][-1]
274
+ assert ids_range[idx][1] <= ids_range[idx + 1][0], (
275
+ f"Found ids overlapping between embeddings '{name1}' " f"and '{name2}'."
276
+ )
277
+
278
+ def add_embeddings(self, embeddings: Optional[Union[dict, List[dict]]]):
279
+ """Add external embeddings to this layer.
280
+ Use case:
281
+ Args:
282
+ embeddings (Union[dict, list[dict]]): The external embeddings to
283
+ be added. Each dict must contain the following 4 fields: 'name'
284
+ (the name of this embedding), 'embedding' (the embedding
285
+ tensor), 'start' (the start token id of this embedding), 'end'
286
+ (the end token id of this embedding). For example:
287
+ `{name: NAME, start: START, end: END, embedding: torch.Tensor}`
288
+ """
289
+ if isinstance(embeddings, dict):
290
+ embeddings = [embeddings]
291
+
292
+ self.external_embeddings += embeddings
293
+ self.check_duplicate_names(self.external_embeddings)
294
+ self.check_ids_overlap(self.external_embeddings)
295
+
296
+ # set for trainable
297
+ added_trainable_emb_info = []
298
+ for embedding in embeddings:
299
+ trainable = embedding.get("trainable", False)
300
+ if trainable:
301
+ name = embedding["name"]
302
+ embedding["embedding"] = torch.nn.Parameter(embedding["embedding"])
303
+ self.trainable_embeddings[name] = embedding["embedding"]
304
+ added_trainable_emb_info.append(name)
305
+
306
+ added_emb_info = [emb["name"] for emb in embeddings]
307
+ added_emb_info = ", ".join(added_emb_info)
308
+ print(f"Successfully add external embeddings: {added_emb_info}.", "current")
309
+
310
+ if added_trainable_emb_info:
311
+ added_trainable_emb_info = ", ".join(added_trainable_emb_info)
312
+ print("Successfully add trainable external embeddings: " f"{added_trainable_emb_info}", "current")
313
+
314
+ def replace_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
315
+ """Replace external input ids to 0.
316
+
317
+ Args:
318
+ input_ids (torch.Tensor): The input ids to be replaced.
319
+
320
+ Returns:
321
+ torch.Tensor: The replaced input ids.
322
+ """
323
+ input_ids_fwd = input_ids.clone()
324
+ input_ids_fwd[input_ids_fwd >= self.num_embeddings] = 0
325
+ return input_ids_fwd
326
+
327
+ def replace_embeddings(
328
+ self, input_ids: torch.Tensor, embedding: torch.Tensor, external_embedding: dict
329
+ ) -> torch.Tensor:
330
+ """Replace external embedding to the embedding layer. Noted that, in
331
+ this function we use `torch.cat` to avoid inplace modification.
332
+
333
+ Args:
334
+ input_ids (torch.Tensor): The original token ids. Shape like
335
+ [LENGTH, ].
336
+ embedding (torch.Tensor): The embedding of token ids after
337
+ `replace_input_ids` function.
338
+ external_embedding (dict): The external embedding to be replaced.
339
+
340
+ Returns:
341
+ torch.Tensor: The replaced embedding.
342
+ """
343
+ new_embedding = []
344
+
345
+ name = external_embedding["name"]
346
+ start = external_embedding["start"]
347
+ end = external_embedding["end"]
348
+ target_ids_to_replace = [i for i in range(start, end)]
349
+ ext_emb = external_embedding["embedding"].to(embedding.device)
350
+
351
+ # do not need to replace
352
+ if not (input_ids == start).any():
353
+ return embedding
354
+
355
+ # start replace
356
+ s_idx, e_idx = 0, 0
357
+ while e_idx < len(input_ids):
358
+ if input_ids[e_idx] == start:
359
+ if e_idx != 0:
360
+ # add embedding do not need to replace
361
+ new_embedding.append(embedding[s_idx:e_idx])
362
+
363
+ # check if the next embedding need to replace is valid
364
+ actually_ids_to_replace = [int(i) for i in input_ids[e_idx: e_idx + end - start]]
365
+ assert actually_ids_to_replace == target_ids_to_replace, (
366
+ f"Invalid 'input_ids' in position: {s_idx} to {e_idx}. "
367
+ f"Expect '{target_ids_to_replace}' for embedding "
368
+ f"'{name}' but found '{actually_ids_to_replace}'."
369
+ )
370
+
371
+ new_embedding.append(ext_emb)
372
+
373
+ s_idx = e_idx + end - start
374
+ e_idx = s_idx + 1
375
+ else:
376
+ e_idx += 1
377
+
378
+ if e_idx == len(input_ids):
379
+ new_embedding.append(embedding[s_idx:e_idx])
380
+
381
+ return torch.cat(new_embedding, dim=0)
382
+
383
+ def forward(self, input_ids: torch.Tensor, external_embeddings: Optional[List[dict]] = None, out_dtype = None):
384
+ """The forward function.
385
+
386
+ Args:
387
+ input_ids (torch.Tensor): The token ids shape like [bz, LENGTH] or
388
+ [LENGTH, ].
389
+ external_embeddings (Optional[List[dict]]): The external
390
+ embeddings. If not passed, only `self.external_embeddings`
391
+ will be used. Defaults to None.
392
+
393
+ input_ids: shape like [bz, LENGTH] or [LENGTH].
394
+ """
395
+ assert input_ids.ndim in [1, 2]
396
+ if input_ids.ndim == 1:
397
+ input_ids = input_ids.unsqueeze(0)
398
+
399
+ if external_embeddings is None and not self.external_embeddings:
400
+ return self.wrapped(input_ids, out_dtype=out_dtype)
401
+
402
+ input_ids_fwd = self.replace_input_ids(input_ids)
403
+ inputs_embeds = self.wrapped(input_ids_fwd)
404
+
405
+ vecs = []
406
+
407
+ if external_embeddings is None:
408
+ external_embeddings = []
409
+ elif isinstance(external_embeddings, dict):
410
+ external_embeddings = [external_embeddings]
411
+ embeddings = self.external_embeddings + external_embeddings
412
+
413
+ for input_id, embedding in zip(input_ids, inputs_embeds):
414
+ new_embedding = embedding
415
+ for external_embedding in embeddings:
416
+ new_embedding = self.replace_embeddings(input_id, new_embedding, external_embedding)
417
+ vecs.append(new_embedding)
418
+
419
+ return torch.stack(vecs).to(out_dtype)
420
+
421
+
422
+ def add_tokens(
423
+ tokenizer, text_encoder, placeholder_tokens: list, initialize_tokens: list = None,
424
+ num_vectors_per_token: int = 1
425
+ ):
426
+ """Add token for training.
427
+
428
+ # TODO: support add tokens as dict, then we can load pretrained tokens.
429
+ """
430
+ if initialize_tokens is not None:
431
+ assert len(initialize_tokens) == len(
432
+ placeholder_tokens
433
+ ), "placeholder_token should be the same length as initialize_token"
434
+ for ii in range(len(placeholder_tokens)):
435
+ tokenizer.add_placeholder_token(placeholder_tokens[ii], num_vec_per_token=num_vectors_per_token)
436
+
437
+ # text_encoder.set_embedding_layer()
438
+ embedding_layer = text_encoder.text_model.embeddings.token_embedding
439
+ text_encoder.text_model.embeddings.token_embedding = EmbeddingLayerWithFixes(embedding_layer)
440
+ embedding_layer = text_encoder.text_model.embeddings.token_embedding
441
+
442
+ assert embedding_layer is not None, (
443
+ "Do not support get embedding layer for current text encoder. " "Please check your configuration."
444
+ )
445
+ initialize_embedding = []
446
+ if initialize_tokens is not None:
447
+ for ii in range(len(placeholder_tokens)):
448
+ init_id = tokenizer(initialize_tokens[ii]).input_ids[1]
449
+ temp_embedding = embedding_layer.weight[init_id]
450
+ initialize_embedding.append(temp_embedding[None, ...].repeat(num_vectors_per_token, 1))
451
+ else:
452
+ for ii in range(len(placeholder_tokens)):
453
+ init_id = tokenizer("a").input_ids[1]
454
+ temp_embedding = embedding_layer.weight[init_id]
455
+ len_emb = temp_embedding.shape[0]
456
+ init_weight = (torch.rand(num_vectors_per_token, len_emb) - 0.5) / 2.0
457
+ initialize_embedding.append(init_weight)
458
+
459
+ # initialize_embedding = torch.cat(initialize_embedding,dim=0)
460
+
461
+ token_info_all = []
462
+ for ii in range(len(placeholder_tokens)):
463
+ token_info = tokenizer.get_token_info(placeholder_tokens[ii])
464
+ token_info["embedding"] = initialize_embedding[ii]
465
+ token_info["trainable"] = True
466
+ token_info_all.append(token_info)
467
+ embedding_layer.add_embeddings(token_info_all)
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/unet_2d_blocks.py ADDED
The diff for this file is too large to render. See raw diff
 
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/unet_2d_condition.py ADDED
@@ -0,0 +1,1359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from dataclasses import dataclass
15
+ from typing import Any, Dict, List, Optional, Tuple, Union
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+ import torch.utils.checkpoint
20
+
21
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
22
+ from diffusers.loaders import PeftAdapterMixin, UNet2DConditionLoadersMixin
23
+ from diffusers.utils import USE_PEFT_BACKEND, BaseOutput, deprecate, logging, scale_lora_layers, unscale_lora_layers
24
+ from diffusers.models.activations import get_activation
25
+ from diffusers.models.attention_processor import (
26
+ ADDED_KV_ATTENTION_PROCESSORS,
27
+ CROSS_ATTENTION_PROCESSORS,
28
+ Attention,
29
+ AttentionProcessor,
30
+ AttnAddedKVProcessor,
31
+ AttnProcessor,
32
+ )
33
+ from diffusers.models.embeddings import (
34
+ GaussianFourierProjection,
35
+ GLIGENTextBoundingboxProjection,
36
+ ImageHintTimeEmbedding,
37
+ ImageProjection,
38
+ ImageTimeEmbedding,
39
+ TextImageProjection,
40
+ TextImageTimeEmbedding,
41
+ TextTimeEmbedding,
42
+ TimestepEmbedding,
43
+ Timesteps,
44
+ )
45
+ from diffusers.models.modeling_utils import ModelMixin
46
+ from .unet_2d_blocks import (
47
+ get_down_block,
48
+ get_mid_block,
49
+ get_up_block,
50
+ )
51
+
52
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
53
+
54
+
55
+ @dataclass
56
+ class UNet2DConditionOutput(BaseOutput):
57
+ """
58
+ The output of [`UNet2DConditionModel`].
59
+
60
+ Args:
61
+ sample (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)`):
62
+ The hidden states output conditioned on `encoder_hidden_states` input. Output of last layer of model.
63
+ """
64
+
65
+ sample: torch.FloatTensor = None
66
+
67
+
68
+ class UNet2DConditionModel(ModelMixin, ConfigMixin, UNet2DConditionLoadersMixin, PeftAdapterMixin):
69
+ r"""
70
+ A conditional 2D UNet model that takes a noisy sample, conditional state, and a timestep and returns a sample
71
+ shaped output.
72
+
73
+ This model inherits from [`ModelMixin`]. Check the superclass documentation for it's generic methods implemented
74
+ for all models (such as downloading or saving).
75
+
76
+ Parameters:
77
+ sample_size (`int` or `Tuple[int, int]`, *optional*, defaults to `None`):
78
+ Height and width of input/output sample.
79
+ in_channels (`int`, *optional*, defaults to 4): Number of channels in the input sample.
80
+ out_channels (`int`, *optional*, defaults to 4): Number of channels in the output.
81
+ center_input_sample (`bool`, *optional*, defaults to `False`): Whether to center the input sample.
82
+ flip_sin_to_cos (`bool`, *optional*, defaults to `True`):
83
+ Whether to flip the sin to cos in the time embedding.
84
+ freq_shift (`int`, *optional*, defaults to 0): The frequency shift to apply to the time embedding.
85
+ down_block_types (`Tuple[str]`, *optional*, defaults to `("CrossAttnDownBlock2D", "CrossAttnDownBlock2D", "CrossAttnDownBlock2D", "DownBlock2D")`):
86
+ The tuple of downsample blocks to use.
87
+ mid_block_type (`str`, *optional*, defaults to `"UNetMidBlock2DCrossAttn"`):
88
+ Block type for middle of UNet, it can be one of `UNetMidBlock2DCrossAttn`, `UNetMidBlock2D`, or
89
+ `UNetMidBlock2DSimpleCrossAttn`. If `None`, the mid block layer is skipped.
90
+ up_block_types (`Tuple[str]`, *optional*, defaults to `("UpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D")`):
91
+ The tuple of upsample blocks to use.
92
+ only_cross_attention(`bool` or `Tuple[bool]`, *optional*, default to `False`):
93
+ Whether to include self-attention in the basic transformer blocks, see
94
+ [`~models.attention.BasicTransformerBlock`].
95
+ block_out_channels (`Tuple[int]`, *optional*, defaults to `(320, 640, 1280, 1280)`):
96
+ The tuple of output channels for each block.
97
+ layers_per_block (`int`, *optional*, defaults to 2): The number of layers per block.
98
+ downsample_padding (`int`, *optional*, defaults to 1): The padding to use for the downsampling convolution.
99
+ mid_block_scale_factor (`float`, *optional*, defaults to 1.0): The scale factor to use for the mid block.
100
+ dropout (`float`, *optional*, defaults to 0.0): The dropout probability to use.
101
+ act_fn (`str`, *optional*, defaults to `"silu"`): The activation function to use.
102
+ norm_num_groups (`int`, *optional*, defaults to 32): The number of groups to use for the normalization.
103
+ If `None`, normalization and activation layers is skipped in post-processing.
104
+ norm_eps (`float`, *optional*, defaults to 1e-5): The epsilon to use for the normalization.
105
+ cross_attention_dim (`int` or `Tuple[int]`, *optional*, defaults to 1280):
106
+ The dimension of the cross attention features.
107
+ transformer_layers_per_block (`int`, `Tuple[int]`, or `Tuple[Tuple]` , *optional*, defaults to 1):
108
+ The number of transformer blocks of type [`~models.attention.BasicTransformerBlock`]. Only relevant for
109
+ [`~models.unet_2d_blocks.CrossAttnDownBlock2D`], [`~models.unet_2d_blocks.CrossAttnUpBlock2D`],
110
+ [`~models.unet_2d_blocks.UNetMidBlock2DCrossAttn`].
111
+ reverse_transformer_layers_per_block : (`Tuple[Tuple]`, *optional*, defaults to None):
112
+ The number of transformer blocks of type [`~models.attention.BasicTransformerBlock`], in the upsampling
113
+ blocks of the U-Net. Only relevant if `transformer_layers_per_block` is of type `Tuple[Tuple]` and for
114
+ [`~models.unet_2d_blocks.CrossAttnDownBlock2D`], [`~models.unet_2d_blocks.CrossAttnUpBlock2D`],
115
+ [`~models.unet_2d_blocks.UNetMidBlock2DCrossAttn`].
116
+ encoder_hid_dim (`int`, *optional*, defaults to None):
117
+ If `encoder_hid_dim_type` is defined, `encoder_hidden_states` will be projected from `encoder_hid_dim`
118
+ dimension to `cross_attention_dim`.
119
+ encoder_hid_dim_type (`str`, *optional*, defaults to `None`):
120
+ If given, the `encoder_hidden_states` and potentially other embeddings are down-projected to text
121
+ embeddings of dimension `cross_attention` according to `encoder_hid_dim_type`.
122
+ attention_head_dim (`int`, *optional*, defaults to 8): The dimension of the attention heads.
123
+ num_attention_heads (`int`, *optional*):
124
+ The number of attention heads. If not defined, defaults to `attention_head_dim`
125
+ resnet_time_scale_shift (`str`, *optional*, defaults to `"default"`): Time scale shift config
126
+ for ResNet blocks (see [`~models.resnet.ResnetBlock2D`]). Choose from `default` or `scale_shift`.
127
+ class_embed_type (`str`, *optional*, defaults to `None`):
128
+ The type of class embedding to use which is ultimately summed with the time embeddings. Choose from `None`,
129
+ `"timestep"`, `"identity"`, `"projection"`, or `"simple_projection"`.
130
+ addition_embed_type (`str`, *optional*, defaults to `None`):
131
+ Configures an optional embedding which will be summed with the time embeddings. Choose from `None` or
132
+ "text". "text" will use the `TextTimeEmbedding` layer.
133
+ addition_time_embed_dim: (`int`, *optional*, defaults to `None`):
134
+ Dimension for the timestep embeddings.
135
+ num_class_embeds (`int`, *optional*, defaults to `None`):
136
+ Input dimension of the learnable embedding matrix to be projected to `time_embed_dim`, when performing
137
+ class conditioning with `class_embed_type` equal to `None`.
138
+ time_embedding_type (`str`, *optional*, defaults to `positional`):
139
+ The type of position embedding to use for timesteps. Choose from `positional` or `fourier`.
140
+ time_embedding_dim (`int`, *optional*, defaults to `None`):
141
+ An optional override for the dimension of the projected time embedding.
142
+ time_embedding_act_fn (`str`, *optional*, defaults to `None`):
143
+ Optional activation function to use only once on the time embeddings before they are passed to the rest of
144
+ the UNet. Choose from `silu`, `mish`, `gelu`, and `swish`.
145
+ timestep_post_act (`str`, *optional*, defaults to `None`):
146
+ The second activation function to use in timestep embedding. Choose from `silu`, `mish` and `gelu`.
147
+ time_cond_proj_dim (`int`, *optional*, defaults to `None`):
148
+ The dimension of `cond_proj` layer in the timestep embedding.
149
+ conv_in_kernel (`int`, *optional*, default to `3`): The kernel size of `conv_in` layer.
150
+ conv_out_kernel (`int`, *optional*, default to `3`): The kernel size of `conv_out` layer.
151
+ projection_class_embeddings_input_dim (`int`, *optional*): The dimension of the `class_labels` input when
152
+ `class_embed_type="projection"`. Required when `class_embed_type="projection"`.
153
+ class_embeddings_concat (`bool`, *optional*, defaults to `False`): Whether to concatenate the time
154
+ embeddings with the class embeddings.
155
+ mid_block_only_cross_attention (`bool`, *optional*, defaults to `None`):
156
+ Whether to use cross attention with the mid block when using the `UNetMidBlock2DSimpleCrossAttn`. If
157
+ `only_cross_attention` is given as a single boolean and `mid_block_only_cross_attention` is `None`, the
158
+ `only_cross_attention` value is used as the value for `mid_block_only_cross_attention`. Default to `False`
159
+ otherwise.
160
+ """
161
+
162
+ _supports_gradient_checkpointing = True
163
+
164
+ @register_to_config
165
+ def __init__(
166
+ self,
167
+ sample_size: Optional[int] = None,
168
+ in_channels: int = 4,
169
+ out_channels: int = 4,
170
+ center_input_sample: bool = False,
171
+ flip_sin_to_cos: bool = True,
172
+ freq_shift: int = 0,
173
+ down_block_types: Tuple[str] = (
174
+ "CrossAttnDownBlock2D",
175
+ "CrossAttnDownBlock2D",
176
+ "CrossAttnDownBlock2D",
177
+ "DownBlock2D",
178
+ ),
179
+ mid_block_type: Optional[str] = "UNetMidBlock2DCrossAttn",
180
+ up_block_types: Tuple[str] = (
181
+ "UpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D"),
182
+ only_cross_attention: Union[bool, Tuple[bool]] = False,
183
+ block_out_channels: Tuple[int] = (320, 640, 1280, 1280),
184
+ layers_per_block: Union[int, Tuple[int]] = 2,
185
+ downsample_padding: int = 1,
186
+ mid_block_scale_factor: float = 1,
187
+ dropout: float = 0.0,
188
+ act_fn: str = "silu",
189
+ norm_num_groups: Optional[int] = 32,
190
+ norm_eps: float = 1e-5,
191
+ cross_attention_dim: Union[int, Tuple[int]] = 1280,
192
+ transformer_layers_per_block: Union[int, Tuple[int], Tuple[Tuple]] = 1,
193
+ reverse_transformer_layers_per_block: Optional[Tuple[Tuple[int]]] = None,
194
+ encoder_hid_dim: Optional[int] = None,
195
+ encoder_hid_dim_type: Optional[str] = None,
196
+ attention_head_dim: Union[int, Tuple[int]] = 8,
197
+ num_attention_heads: Optional[Union[int, Tuple[int]]] = None,
198
+ dual_cross_attention: bool = False,
199
+ use_linear_projection: bool = False,
200
+ class_embed_type: Optional[str] = None,
201
+ addition_embed_type: Optional[str] = None,
202
+ addition_time_embed_dim: Optional[int] = None,
203
+ num_class_embeds: Optional[int] = None,
204
+ upcast_attention: bool = False,
205
+ resnet_time_scale_shift: str = "default",
206
+ resnet_skip_time_act: bool = False,
207
+ resnet_out_scale_factor: float = 1.0,
208
+ time_embedding_type: str = "positional",
209
+ time_embedding_dim: Optional[int] = None,
210
+ time_embedding_act_fn: Optional[str] = None,
211
+ timestep_post_act: Optional[str] = None,
212
+ time_cond_proj_dim: Optional[int] = None,
213
+ conv_in_kernel: int = 3,
214
+ conv_out_kernel: int = 3,
215
+ projection_class_embeddings_input_dim: Optional[int] = None,
216
+ attention_type: str = "default",
217
+ class_embeddings_concat: bool = False,
218
+ mid_block_only_cross_attention: Optional[bool] = None,
219
+ cross_attention_norm: Optional[str] = None,
220
+ addition_embed_type_num_heads: int = 64,
221
+ ):
222
+ super().__init__()
223
+
224
+ self.sample_size = sample_size
225
+
226
+ if num_attention_heads is not None:
227
+ raise ValueError(
228
+ "At the moment it is not possible to define the number of attention heads via `num_attention_heads` because of a naming issue as described in https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131. Passing `num_attention_heads` will only be supported in diffusers v0.19."
229
+ )
230
+
231
+ # If `num_attention_heads` is not defined (which is the case for most models)
232
+ # it will default to `attention_head_dim`. This looks weird upon first reading it and it is.
233
+ # The reason for this behavior is to correct for incorrectly named variables that were introduced
234
+ # when this library was created. The incorrect naming was only discovered much later in https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131
235
+ # Changing `attention_head_dim` to `num_attention_heads` for 40,000+ configurations is too backwards breaking
236
+ # which is why we correct for the naming here.
237
+ num_attention_heads = num_attention_heads or attention_head_dim
238
+
239
+ # Check inputs
240
+ self._check_config(
241
+ down_block_types=down_block_types,
242
+ up_block_types=up_block_types,
243
+ only_cross_attention=only_cross_attention,
244
+ block_out_channels=block_out_channels,
245
+ layers_per_block=layers_per_block,
246
+ cross_attention_dim=cross_attention_dim,
247
+ transformer_layers_per_block=transformer_layers_per_block,
248
+ reverse_transformer_layers_per_block=reverse_transformer_layers_per_block,
249
+ attention_head_dim=attention_head_dim,
250
+ num_attention_heads=num_attention_heads,
251
+ )
252
+
253
+ # input
254
+ conv_in_padding = (conv_in_kernel - 1) // 2
255
+ self.conv_in = nn.Conv2d(
256
+ in_channels, block_out_channels[0], kernel_size=conv_in_kernel, padding=conv_in_padding
257
+ )
258
+
259
+ # time
260
+ time_embed_dim, timestep_input_dim = self._set_time_proj(
261
+ time_embedding_type,
262
+ block_out_channels=block_out_channels,
263
+ flip_sin_to_cos=flip_sin_to_cos,
264
+ freq_shift=freq_shift,
265
+ time_embedding_dim=time_embedding_dim,
266
+ )
267
+
268
+ self.time_embedding = TimestepEmbedding(
269
+ timestep_input_dim,
270
+ time_embed_dim,
271
+ act_fn=act_fn,
272
+ post_act_fn=timestep_post_act,
273
+ cond_proj_dim=time_cond_proj_dim,
274
+ )
275
+
276
+ self._set_encoder_hid_proj(
277
+ encoder_hid_dim_type,
278
+ cross_attention_dim=cross_attention_dim,
279
+ encoder_hid_dim=encoder_hid_dim,
280
+ )
281
+
282
+ # class embedding
283
+ self._set_class_embedding(
284
+ class_embed_type,
285
+ act_fn=act_fn,
286
+ num_class_embeds=num_class_embeds,
287
+ projection_class_embeddings_input_dim=projection_class_embeddings_input_dim,
288
+ time_embed_dim=time_embed_dim,
289
+ timestep_input_dim=timestep_input_dim,
290
+ )
291
+
292
+ self._set_add_embedding(
293
+ addition_embed_type,
294
+ addition_embed_type_num_heads=addition_embed_type_num_heads,
295
+ addition_time_embed_dim=addition_time_embed_dim,
296
+ cross_attention_dim=cross_attention_dim,
297
+ encoder_hid_dim=encoder_hid_dim,
298
+ flip_sin_to_cos=flip_sin_to_cos,
299
+ freq_shift=freq_shift,
300
+ projection_class_embeddings_input_dim=projection_class_embeddings_input_dim,
301
+ time_embed_dim=time_embed_dim,
302
+ )
303
+
304
+ if time_embedding_act_fn is None:
305
+ self.time_embed_act = None
306
+ else:
307
+ self.time_embed_act = get_activation(time_embedding_act_fn)
308
+
309
+ self.down_blocks = nn.ModuleList([])
310
+ self.up_blocks = nn.ModuleList([])
311
+
312
+ if isinstance(only_cross_attention, bool):
313
+ if mid_block_only_cross_attention is None:
314
+ mid_block_only_cross_attention = only_cross_attention
315
+
316
+ only_cross_attention = [only_cross_attention] * len(down_block_types)
317
+
318
+ if mid_block_only_cross_attention is None:
319
+ mid_block_only_cross_attention = False
320
+
321
+ if isinstance(num_attention_heads, int):
322
+ num_attention_heads = (num_attention_heads,) * len(down_block_types)
323
+
324
+ if isinstance(attention_head_dim, int):
325
+ attention_head_dim = (attention_head_dim,) * len(down_block_types)
326
+
327
+ if isinstance(cross_attention_dim, int):
328
+ cross_attention_dim = (cross_attention_dim,) * len(down_block_types)
329
+
330
+ if isinstance(layers_per_block, int):
331
+ layers_per_block = [layers_per_block] * len(down_block_types)
332
+
333
+ if isinstance(transformer_layers_per_block, int):
334
+ transformer_layers_per_block = [transformer_layers_per_block] * len(down_block_types)
335
+
336
+ if class_embeddings_concat:
337
+ # The time embeddings are concatenated with the class embeddings. The dimension of the
338
+ # time embeddings passed to the down, middle, and up blocks is twice the dimension of the
339
+ # regular time embeddings
340
+ blocks_time_embed_dim = time_embed_dim * 2
341
+ else:
342
+ blocks_time_embed_dim = time_embed_dim
343
+
344
+ # down
345
+ output_channel = block_out_channels[0]
346
+ for i, down_block_type in enumerate(down_block_types):
347
+ input_channel = output_channel
348
+ output_channel = block_out_channels[i]
349
+ is_final_block = i == len(block_out_channels) - 1
350
+
351
+ down_block = get_down_block(
352
+ down_block_type,
353
+ num_layers=layers_per_block[i],
354
+ transformer_layers_per_block=transformer_layers_per_block[i],
355
+ in_channels=input_channel,
356
+ out_channels=output_channel,
357
+ temb_channels=blocks_time_embed_dim,
358
+ add_downsample=not is_final_block,
359
+ resnet_eps=norm_eps,
360
+ resnet_act_fn=act_fn,
361
+ resnet_groups=norm_num_groups,
362
+ cross_attention_dim=cross_attention_dim[i],
363
+ num_attention_heads=num_attention_heads[i],
364
+ downsample_padding=downsample_padding,
365
+ dual_cross_attention=dual_cross_attention,
366
+ use_linear_projection=use_linear_projection,
367
+ only_cross_attention=only_cross_attention[i],
368
+ upcast_attention=upcast_attention,
369
+ resnet_time_scale_shift=resnet_time_scale_shift,
370
+ attention_type=attention_type,
371
+ resnet_skip_time_act=resnet_skip_time_act,
372
+ resnet_out_scale_factor=resnet_out_scale_factor,
373
+ cross_attention_norm=cross_attention_norm,
374
+ attention_head_dim=attention_head_dim[i] if attention_head_dim[i] is not None else output_channel,
375
+ dropout=dropout,
376
+ )
377
+ self.down_blocks.append(down_block)
378
+
379
+ # mid
380
+ self.mid_block = get_mid_block(
381
+ mid_block_type,
382
+ temb_channels=blocks_time_embed_dim,
383
+ in_channels=block_out_channels[-1],
384
+ resnet_eps=norm_eps,
385
+ resnet_act_fn=act_fn,
386
+ resnet_groups=norm_num_groups,
387
+ output_scale_factor=mid_block_scale_factor,
388
+ transformer_layers_per_block=transformer_layers_per_block[-1],
389
+ num_attention_heads=num_attention_heads[-1],
390
+ cross_attention_dim=cross_attention_dim[-1],
391
+ dual_cross_attention=dual_cross_attention,
392
+ use_linear_projection=use_linear_projection,
393
+ mid_block_only_cross_attention=mid_block_only_cross_attention,
394
+ upcast_attention=upcast_attention,
395
+ resnet_time_scale_shift=resnet_time_scale_shift,
396
+ attention_type=attention_type,
397
+ resnet_skip_time_act=resnet_skip_time_act,
398
+ cross_attention_norm=cross_attention_norm,
399
+ attention_head_dim=attention_head_dim[-1],
400
+ dropout=dropout,
401
+ )
402
+
403
+ # count how many layers upsample the images
404
+ self.num_upsamplers = 0
405
+
406
+ # up
407
+ reversed_block_out_channels = list(reversed(block_out_channels))
408
+ reversed_num_attention_heads = list(reversed(num_attention_heads))
409
+ reversed_layers_per_block = list(reversed(layers_per_block))
410
+ reversed_cross_attention_dim = list(reversed(cross_attention_dim))
411
+ reversed_transformer_layers_per_block = (
412
+ list(reversed(transformer_layers_per_block))
413
+ if reverse_transformer_layers_per_block is None
414
+ else reverse_transformer_layers_per_block
415
+ )
416
+ only_cross_attention = list(reversed(only_cross_attention))
417
+
418
+ output_channel = reversed_block_out_channels[0]
419
+ for i, up_block_type in enumerate(up_block_types):
420
+ is_final_block = i == len(block_out_channels) - 1
421
+
422
+ prev_output_channel = output_channel
423
+ output_channel = reversed_block_out_channels[i]
424
+ input_channel = reversed_block_out_channels[min(i + 1, len(block_out_channels) - 1)]
425
+
426
+ # add upsample block for all BUT final layer
427
+ if not is_final_block:
428
+ add_upsample = True
429
+ self.num_upsamplers += 1
430
+ else:
431
+ add_upsample = False
432
+
433
+ up_block = get_up_block(
434
+ up_block_type,
435
+ num_layers=reversed_layers_per_block[i] + 1,
436
+ transformer_layers_per_block=reversed_transformer_layers_per_block[i],
437
+ in_channels=input_channel,
438
+ out_channels=output_channel,
439
+ prev_output_channel=prev_output_channel,
440
+ temb_channels=blocks_time_embed_dim,
441
+ add_upsample=add_upsample,
442
+ resnet_eps=norm_eps,
443
+ resnet_act_fn=act_fn,
444
+ resolution_idx=i,
445
+ resnet_groups=norm_num_groups,
446
+ cross_attention_dim=reversed_cross_attention_dim[i],
447
+ num_attention_heads=reversed_num_attention_heads[i],
448
+ dual_cross_attention=dual_cross_attention,
449
+ use_linear_projection=use_linear_projection,
450
+ only_cross_attention=only_cross_attention[i],
451
+ upcast_attention=upcast_attention,
452
+ resnet_time_scale_shift=resnet_time_scale_shift,
453
+ attention_type=attention_type,
454
+ resnet_skip_time_act=resnet_skip_time_act,
455
+ resnet_out_scale_factor=resnet_out_scale_factor,
456
+ cross_attention_norm=cross_attention_norm,
457
+ attention_head_dim=attention_head_dim[i] if attention_head_dim[i] is not None else output_channel,
458
+ dropout=dropout,
459
+ )
460
+ self.up_blocks.append(up_block)
461
+ prev_output_channel = output_channel
462
+
463
+ # out
464
+ if norm_num_groups is not None:
465
+ self.conv_norm_out = nn.GroupNorm(
466
+ num_channels=block_out_channels[0], num_groups=norm_num_groups, eps=norm_eps
467
+ )
468
+
469
+ self.conv_act = get_activation(act_fn)
470
+
471
+ else:
472
+ self.conv_norm_out = None
473
+ self.conv_act = None
474
+
475
+ conv_out_padding = (conv_out_kernel - 1) // 2
476
+ self.conv_out = nn.Conv2d(
477
+ block_out_channels[0], out_channels, kernel_size=conv_out_kernel, padding=conv_out_padding
478
+ )
479
+
480
+ self._set_pos_net_if_use_gligen(attention_type=attention_type, cross_attention_dim=cross_attention_dim)
481
+
482
+ def _check_config(
483
+ self,
484
+ down_block_types: Tuple[str],
485
+ up_block_types: Tuple[str],
486
+ only_cross_attention: Union[bool, Tuple[bool]],
487
+ block_out_channels: Tuple[int],
488
+ layers_per_block: Union[int, Tuple[int]],
489
+ cross_attention_dim: Union[int, Tuple[int]],
490
+ transformer_layers_per_block: Union[int, Tuple[int], Tuple[Tuple[int]]],
491
+ reverse_transformer_layers_per_block: bool,
492
+ attention_head_dim: int,
493
+ num_attention_heads: Optional[Union[int, Tuple[int]]],
494
+ ):
495
+ if len(down_block_types) != len(up_block_types):
496
+ raise ValueError(
497
+ f"Must provide the same number of `down_block_types` as `up_block_types`. `down_block_types`: {down_block_types}. `up_block_types`: {up_block_types}."
498
+ )
499
+
500
+ if len(block_out_channels) != len(down_block_types):
501
+ raise ValueError(
502
+ f"Must provide the same number of `block_out_channels` as `down_block_types`. `block_out_channels`: {block_out_channels}. `down_block_types`: {down_block_types}."
503
+ )
504
+
505
+ if not isinstance(only_cross_attention, bool) and len(only_cross_attention) != len(down_block_types):
506
+ raise ValueError(
507
+ f"Must provide the same number of `only_cross_attention` as `down_block_types`. `only_cross_attention`: {only_cross_attention}. `down_block_types`: {down_block_types}."
508
+ )
509
+
510
+ if not isinstance(num_attention_heads, int) and len(num_attention_heads) != len(down_block_types):
511
+ raise ValueError(
512
+ f"Must provide the same number of `num_attention_heads` as `down_block_types`. `num_attention_heads`: {num_attention_heads}. `down_block_types`: {down_block_types}."
513
+ )
514
+
515
+ if not isinstance(attention_head_dim, int) and len(attention_head_dim) != len(down_block_types):
516
+ raise ValueError(
517
+ f"Must provide the same number of `attention_head_dim` as `down_block_types`. `attention_head_dim`: {attention_head_dim}. `down_block_types`: {down_block_types}."
518
+ )
519
+
520
+ if isinstance(cross_attention_dim, list) and len(cross_attention_dim) != len(down_block_types):
521
+ raise ValueError(
522
+ f"Must provide the same number of `cross_attention_dim` as `down_block_types`. `cross_attention_dim`: {cross_attention_dim}. `down_block_types`: {down_block_types}."
523
+ )
524
+
525
+ if not isinstance(layers_per_block, int) and len(layers_per_block) != len(down_block_types):
526
+ raise ValueError(
527
+ f"Must provide the same number of `layers_per_block` as `down_block_types`. `layers_per_block`: {layers_per_block}. `down_block_types`: {down_block_types}."
528
+ )
529
+ if isinstance(transformer_layers_per_block, list) and reverse_transformer_layers_per_block is None:
530
+ for layer_number_per_block in transformer_layers_per_block:
531
+ if isinstance(layer_number_per_block, list):
532
+ raise ValueError("Must provide 'reverse_transformer_layers_per_block` if using asymmetrical UNet.")
533
+
534
+ def _set_time_proj(
535
+ self,
536
+ time_embedding_type: str,
537
+ block_out_channels: int,
538
+ flip_sin_to_cos: bool,
539
+ freq_shift: float,
540
+ time_embedding_dim: int,
541
+ ) -> Tuple[int, int]:
542
+ if time_embedding_type == "fourier":
543
+ time_embed_dim = time_embedding_dim or block_out_channels[0] * 2
544
+ if time_embed_dim % 2 != 0:
545
+ raise ValueError(f"`time_embed_dim` should be divisible by 2, but is {time_embed_dim}.")
546
+ self.time_proj = GaussianFourierProjection(
547
+ time_embed_dim // 2, set_W_to_weight=False, log=False, flip_sin_to_cos=flip_sin_to_cos
548
+ )
549
+ timestep_input_dim = time_embed_dim
550
+ elif time_embedding_type == "positional":
551
+ time_embed_dim = time_embedding_dim or block_out_channels[0] * 4
552
+
553
+ self.time_proj = Timesteps(block_out_channels[0], flip_sin_to_cos, freq_shift)
554
+ timestep_input_dim = block_out_channels[0]
555
+ else:
556
+ raise ValueError(
557
+ f"{time_embedding_type} does not exist. Please make sure to use one of `fourier` or `positional`."
558
+ )
559
+
560
+ return time_embed_dim, timestep_input_dim
561
+
562
+ def _set_encoder_hid_proj(
563
+ self,
564
+ encoder_hid_dim_type: Optional[str],
565
+ cross_attention_dim: Union[int, Tuple[int]],
566
+ encoder_hid_dim: Optional[int],
567
+ ):
568
+ if encoder_hid_dim_type is None and encoder_hid_dim is not None:
569
+ encoder_hid_dim_type = "text_proj"
570
+ self.register_to_config(encoder_hid_dim_type=encoder_hid_dim_type)
571
+ logger.info("encoder_hid_dim_type defaults to 'text_proj' as `encoder_hid_dim` is defined.")
572
+
573
+ if encoder_hid_dim is None and encoder_hid_dim_type is not None:
574
+ raise ValueError(
575
+ f"`encoder_hid_dim` has to be defined when `encoder_hid_dim_type` is set to {encoder_hid_dim_type}."
576
+ )
577
+
578
+ if encoder_hid_dim_type == "text_proj":
579
+ self.encoder_hid_proj = nn.Linear(encoder_hid_dim, cross_attention_dim)
580
+ elif encoder_hid_dim_type == "text_image_proj":
581
+ # image_embed_dim DOESN'T have to be `cross_attention_dim`. To not clutter the __init__ too much
582
+ # they are set to `cross_attention_dim` here as this is exactly the required dimension for the currently only use
583
+ # case when `addition_embed_type == "text_image_proj"` (Kandinsky 2.1)`
584
+ self.encoder_hid_proj = TextImageProjection(
585
+ text_embed_dim=encoder_hid_dim,
586
+ image_embed_dim=cross_attention_dim,
587
+ cross_attention_dim=cross_attention_dim,
588
+ )
589
+ elif encoder_hid_dim_type == "image_proj":
590
+ # Kandinsky 2.2
591
+ self.encoder_hid_proj = ImageProjection(
592
+ image_embed_dim=encoder_hid_dim,
593
+ cross_attention_dim=cross_attention_dim,
594
+ )
595
+ elif encoder_hid_dim_type is not None:
596
+ raise ValueError(
597
+ f"encoder_hid_dim_type: {encoder_hid_dim_type} must be None, 'text_proj' or 'text_image_proj'."
598
+ )
599
+ else:
600
+ self.encoder_hid_proj = None
601
+
602
+ def _set_class_embedding(
603
+ self,
604
+ class_embed_type: Optional[str],
605
+ act_fn: str,
606
+ num_class_embeds: Optional[int],
607
+ projection_class_embeddings_input_dim: Optional[int],
608
+ time_embed_dim: int,
609
+ timestep_input_dim: int,
610
+ ):
611
+ if class_embed_type is None and num_class_embeds is not None:
612
+ self.class_embedding = nn.Embedding(num_class_embeds, time_embed_dim)
613
+ elif class_embed_type == "timestep":
614
+ self.class_embedding = TimestepEmbedding(timestep_input_dim, time_embed_dim, act_fn=act_fn)
615
+ elif class_embed_type == "identity":
616
+ self.class_embedding = nn.Identity(time_embed_dim, time_embed_dim)
617
+ elif class_embed_type == "projection":
618
+ if projection_class_embeddings_input_dim is None:
619
+ raise ValueError(
620
+ "`class_embed_type`: 'projection' requires `projection_class_embeddings_input_dim` be set"
621
+ )
622
+ # The projection `class_embed_type` is the same as the timestep `class_embed_type` except
623
+ # 1. the `class_labels` inputs are not first converted to sinusoidal embeddings
624
+ # 2. it projects from an arbitrary input dimension.
625
+ #
626
+ # Note that `TimestepEmbedding` is quite general, being mainly linear layers and activations.
627
+ # When used for embedding actual timesteps, the timesteps are first converted to sinusoidal embeddings.
628
+ # As a result, `TimestepEmbedding` can be passed arbitrary vectors.
629
+ self.class_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
630
+ elif class_embed_type == "simple_projection":
631
+ if projection_class_embeddings_input_dim is None:
632
+ raise ValueError(
633
+ "`class_embed_type`: 'simple_projection' requires `projection_class_embeddings_input_dim` be set"
634
+ )
635
+ self.class_embedding = nn.Linear(projection_class_embeddings_input_dim, time_embed_dim)
636
+ else:
637
+ self.class_embedding = None
638
+
639
+ def _set_add_embedding(
640
+ self,
641
+ addition_embed_type: str,
642
+ addition_embed_type_num_heads: int,
643
+ addition_time_embed_dim: Optional[int],
644
+ flip_sin_to_cos: bool,
645
+ freq_shift: float,
646
+ cross_attention_dim: Optional[int],
647
+ encoder_hid_dim: Optional[int],
648
+ projection_class_embeddings_input_dim: Optional[int],
649
+ time_embed_dim: int,
650
+ ):
651
+ if addition_embed_type == "text":
652
+ if encoder_hid_dim is not None:
653
+ text_time_embedding_from_dim = encoder_hid_dim
654
+ else:
655
+ text_time_embedding_from_dim = cross_attention_dim
656
+
657
+ self.add_embedding = TextTimeEmbedding(
658
+ text_time_embedding_from_dim, time_embed_dim, num_heads=addition_embed_type_num_heads
659
+ )
660
+ elif addition_embed_type == "text_image":
661
+ # text_embed_dim and image_embed_dim DON'T have to be `cross_attention_dim`. To not clutter the __init__ too much
662
+ # they are set to `cross_attention_dim` here as this is exactly the required dimension for the currently only use
663
+ # case when `addition_embed_type == "text_image"` (Kandinsky 2.1)`
664
+ self.add_embedding = TextImageTimeEmbedding(
665
+ text_embed_dim=cross_attention_dim, image_embed_dim=cross_attention_dim, time_embed_dim=time_embed_dim
666
+ )
667
+ elif addition_embed_type == "text_time":
668
+ self.add_time_proj = Timesteps(addition_time_embed_dim, flip_sin_to_cos, freq_shift)
669
+ self.add_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
670
+ elif addition_embed_type == "image":
671
+ # Kandinsky 2.2
672
+ self.add_embedding = ImageTimeEmbedding(image_embed_dim=encoder_hid_dim, time_embed_dim=time_embed_dim)
673
+ elif addition_embed_type == "image_hint":
674
+ # Kandinsky 2.2 ControlNet
675
+ self.add_embedding = ImageHintTimeEmbedding(image_embed_dim=encoder_hid_dim, time_embed_dim=time_embed_dim)
676
+ elif addition_embed_type is not None:
677
+ raise ValueError(f"addition_embed_type: {addition_embed_type} must be None, 'text' or 'text_image'.")
678
+
679
+ def _set_pos_net_if_use_gligen(self, attention_type: str, cross_attention_dim: int):
680
+ if attention_type in ["gated", "gated-text-image"]:
681
+ positive_len = 768
682
+ if isinstance(cross_attention_dim, int):
683
+ positive_len = cross_attention_dim
684
+ elif isinstance(cross_attention_dim, tuple) or isinstance(cross_attention_dim, list):
685
+ positive_len = cross_attention_dim[0]
686
+
687
+ feature_type = "text-only" if attention_type == "gated" else "text-image"
688
+ self.position_net = GLIGENTextBoundingboxProjection(
689
+ positive_len=positive_len, out_dim=cross_attention_dim, feature_type=feature_type
690
+ )
691
+
692
+ @property
693
+ def attn_processors(self) -> Dict[str, AttentionProcessor]:
694
+ r"""
695
+ Returns:
696
+ `dict` of attention processors: A dictionary containing all attention processors used in the model with
697
+ indexed by its weight name.
698
+ """
699
+ # set recursively
700
+ processors = {}
701
+
702
+ def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
703
+ if hasattr(module, "get_processor"):
704
+ processors[f"{name}.processor"] = module.get_processor(return_deprecated_lora=True)
705
+
706
+ for sub_name, child in module.named_children():
707
+ fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
708
+
709
+ return processors
710
+
711
+ for name, module in self.named_children():
712
+ fn_recursive_add_processors(name, module, processors)
713
+
714
+ return processors
715
+
716
+ def set_attn_processor(self, processor: Union[AttentionProcessor, Dict[str, AttentionProcessor]]):
717
+ r"""
718
+ Sets the attention processor to use to compute attention.
719
+
720
+ Parameters:
721
+ processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`):
722
+ The instantiated processor class or a dictionary of processor classes that will be set as the processor
723
+ for **all** `Attention` layers.
724
+
725
+ If `processor` is a dict, the key needs to define the path to the corresponding cross attention
726
+ processor. This is strongly recommended when setting trainable attention processors.
727
+
728
+ """
729
+ count = len(self.attn_processors.keys())
730
+
731
+ if isinstance(processor, dict) and len(processor) != count:
732
+ raise ValueError(
733
+ f"A dict of processors was passed, but the number of processors {len(processor)} does not match the"
734
+ f" number of attention layers: {count}. Please make sure to pass {count} processor classes."
735
+ )
736
+
737
+ def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor):
738
+ if hasattr(module, "set_processor"):
739
+ if not isinstance(processor, dict):
740
+ module.set_processor(processor)
741
+ else:
742
+ module.set_processor(processor.pop(f"{name}.processor"))
743
+
744
+ for sub_name, child in module.named_children():
745
+ fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor)
746
+
747
+ for name, module in self.named_children():
748
+ fn_recursive_attn_processor(name, module, processor)
749
+
750
+ def set_default_attn_processor(self):
751
+ """
752
+ Disables custom attention processors and sets the default attention implementation.
753
+ """
754
+ if all(proc.__class__ in ADDED_KV_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
755
+ processor = AttnAddedKVProcessor()
756
+ elif all(proc.__class__ in CROSS_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
757
+ processor = AttnProcessor()
758
+ else:
759
+ raise ValueError(
760
+ f"Cannot call `set_default_attn_processor` when attention processors are of type {next(iter(self.attn_processors.values()))}"
761
+ )
762
+
763
+ self.set_attn_processor(processor)
764
+
765
+ def set_attention_slice(self, slice_size: Union[str, int, List[int]] = "auto"):
766
+ r"""
767
+ Enable sliced attention computation.
768
+
769
+ When this option is enabled, the attention module splits the input tensor in slices to compute attention in
770
+ several steps. This is useful for saving some memory in exchange for a small decrease in speed.
771
+
772
+ Args:
773
+ slice_size (`str` or `int` or `list(int)`, *optional*, defaults to `"auto"`):
774
+ When `"auto"`, input to the attention heads is halved, so attention is computed in two steps. If
775
+ `"max"`, maximum amount of memory is saved by running only one slice at a time. If a number is
776
+ provided, uses as many slices as `attention_head_dim // slice_size`. In this case, `attention_head_dim`
777
+ must be a multiple of `slice_size`.
778
+ """
779
+ sliceable_head_dims = []
780
+
781
+ def fn_recursive_retrieve_sliceable_dims(module: torch.nn.Module):
782
+ if hasattr(module, "set_attention_slice"):
783
+ sliceable_head_dims.append(module.sliceable_head_dim)
784
+
785
+ for child in module.children():
786
+ fn_recursive_retrieve_sliceable_dims(child)
787
+
788
+ # retrieve number of attention layers
789
+ for module in self.children():
790
+ fn_recursive_retrieve_sliceable_dims(module)
791
+
792
+ num_sliceable_layers = len(sliceable_head_dims)
793
+
794
+ if slice_size == "auto":
795
+ # half the attention head size is usually a good trade-off between
796
+ # speed and memory
797
+ slice_size = [dim // 2 for dim in sliceable_head_dims]
798
+ elif slice_size == "max":
799
+ # make smallest slice possible
800
+ slice_size = num_sliceable_layers * [1]
801
+
802
+ slice_size = num_sliceable_layers * [slice_size] if not isinstance(slice_size, list) else slice_size
803
+
804
+ if len(slice_size) != len(sliceable_head_dims):
805
+ raise ValueError(
806
+ f"You have provided {len(slice_size)}, but {self.config} has {len(sliceable_head_dims)} different"
807
+ f" attention layers. Make sure to match `len(slice_size)` to be {len(sliceable_head_dims)}."
808
+ )
809
+
810
+ for i in range(len(slice_size)):
811
+ size = slice_size[i]
812
+ dim = sliceable_head_dims[i]
813
+ if size is not None and size > dim:
814
+ raise ValueError(f"size {size} has to be smaller or equal to {dim}.")
815
+
816
+ # Recursively walk through all the children.
817
+ # Any children which exposes the set_attention_slice method
818
+ # gets the message
819
+ def fn_recursive_set_attention_slice(module: torch.nn.Module, slice_size: List[int]):
820
+ if hasattr(module, "set_attention_slice"):
821
+ module.set_attention_slice(slice_size.pop())
822
+
823
+ for child in module.children():
824
+ fn_recursive_set_attention_slice(child, slice_size)
825
+
826
+ reversed_slice_size = list(reversed(slice_size))
827
+ for module in self.children():
828
+ fn_recursive_set_attention_slice(module, reversed_slice_size)
829
+
830
+ def _set_gradient_checkpointing(self, module, value=False):
831
+ if hasattr(module, "gradient_checkpointing"):
832
+ module.gradient_checkpointing = value
833
+
834
+ def enable_freeu(self, s1: float, s2: float, b1: float, b2: float):
835
+ r"""Enables the FreeU mechanism from https://arxiv.org/abs/2309.11497.
836
+
837
+ The suffixes after the scaling factors represent the stage blocks where they are being applied.
838
+
839
+ Please refer to the [official repository](https://github.com/ChenyangSi/FreeU) for combinations of values that
840
+ are known to work well for different pipelines such as Stable Diffusion v1, v2, and Stable Diffusion XL.
841
+
842
+ Args:
843
+ s1 (`float`):
844
+ Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to
845
+ mitigate the "oversmoothing effect" in the enhanced denoising process.
846
+ s2 (`float`):
847
+ Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to
848
+ mitigate the "oversmoothing effect" in the enhanced denoising process.
849
+ b1 (`float`): Scaling factor for stage 1 to amplify the contributions of backbone features.
850
+ b2 (`float`): Scaling factor for stage 2 to amplify the contributions of backbone features.
851
+ """
852
+ for i, upsample_block in enumerate(self.up_blocks):
853
+ setattr(upsample_block, "s1", s1)
854
+ setattr(upsample_block, "s2", s2)
855
+ setattr(upsample_block, "b1", b1)
856
+ setattr(upsample_block, "b2", b2)
857
+
858
+ def disable_freeu(self):
859
+ """Disables the FreeU mechanism."""
860
+ freeu_keys = {"s1", "s2", "b1", "b2"}
861
+ for i, upsample_block in enumerate(self.up_blocks):
862
+ for k in freeu_keys:
863
+ if hasattr(upsample_block, k) or getattr(upsample_block, k, None) is not None:
864
+ setattr(upsample_block, k, None)
865
+
866
+ def fuse_qkv_projections(self):
867
+ """
868
+ Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query, key, value)
869
+ are fused. For cross-attention modules, key and value projection matrices are fused.
870
+
871
+ <Tip warning={true}>
872
+
873
+ This API is 🧪 experimental.
874
+
875
+ </Tip>
876
+ """
877
+ self.original_attn_processors = None
878
+
879
+ for _, attn_processor in self.attn_processors.items():
880
+ if "Added" in str(attn_processor.__class__.__name__):
881
+ raise ValueError("`fuse_qkv_projections()` is not supported for models having added KV projections.")
882
+
883
+ self.original_attn_processors = self.attn_processors
884
+
885
+ for module in self.modules():
886
+ if isinstance(module, Attention):
887
+ module.fuse_projections(fuse=True)
888
+
889
+ def unfuse_qkv_projections(self):
890
+ """Disables the fused QKV projection if enabled.
891
+
892
+ <Tip warning={true}>
893
+
894
+ This API is 🧪 experimental.
895
+
896
+ </Tip>
897
+
898
+ """
899
+ if self.original_attn_processors is not None:
900
+ self.set_attn_processor(self.original_attn_processors)
901
+
902
+ def unload_lora(self):
903
+ """Unloads LoRA weights."""
904
+ deprecate(
905
+ "unload_lora",
906
+ "0.28.0",
907
+ "Calling `unload_lora()` is deprecated and will be removed in a future version. Please install `peft` and then call `disable_adapters().",
908
+ )
909
+ for module in self.modules():
910
+ if hasattr(module, "set_lora_layer"):
911
+ module.set_lora_layer(None)
912
+
913
+ def get_time_embed(
914
+ self, sample: torch.Tensor, timestep: Union[torch.Tensor, float, int]
915
+ ) -> Optional[torch.Tensor]:
916
+ timesteps = timestep
917
+ if not torch.is_tensor(timesteps):
918
+ # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can
919
+ # This would be a good case for the `match` statement (Python 3.10+)
920
+ is_mps = sample.device.type == "mps"
921
+ if isinstance(timestep, float):
922
+ dtype = torch.float32 if is_mps else torch.float64
923
+ else:
924
+ dtype = torch.int32 if is_mps else torch.int64
925
+ timesteps = torch.tensor([timesteps], dtype=dtype, device=sample.device)
926
+ elif len(timesteps.shape) == 0:
927
+ timesteps = timesteps[None].to(sample.device)
928
+
929
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
930
+ timesteps = timesteps.expand(sample.shape[0])
931
+
932
+ t_emb = self.time_proj(timesteps)
933
+ # `Timesteps` does not contain any weights and will always return f32 tensors
934
+ # but time_embedding might actually be running in fp16. so we need to cast here.
935
+ # there might be better ways to encapsulate this.
936
+ t_emb = t_emb.to(dtype=sample.dtype)
937
+ return t_emb
938
+
939
+ def get_class_embed(self, sample: torch.Tensor, class_labels: Optional[torch.Tensor]) -> Optional[torch.Tensor]:
940
+ class_emb = None
941
+ if self.class_embedding is not None:
942
+ if class_labels is None:
943
+ raise ValueError("class_labels should be provided when num_class_embeds > 0")
944
+
945
+ if self.config.class_embed_type == "timestep":
946
+ class_labels = self.time_proj(class_labels)
947
+
948
+ # `Timesteps` does not contain any weights and will always return f32 tensors
949
+ # there might be better ways to encapsulate this.
950
+ class_labels = class_labels.to(dtype=sample.dtype)
951
+
952
+ class_emb = self.class_embedding(class_labels).to(dtype=sample.dtype)
953
+ return class_emb
954
+
955
+ def get_aug_embed(
956
+ self, emb: torch.Tensor, encoder_hidden_states: torch.Tensor, added_cond_kwargs: Dict[str, Any]
957
+ ) -> Optional[torch.Tensor]:
958
+ aug_emb = None
959
+ if self.config.addition_embed_type == "text":
960
+ aug_emb = self.add_embedding(encoder_hidden_states)
961
+ elif self.config.addition_embed_type == "text_image":
962
+ # Kandinsky 2.1 - style
963
+ if "image_embeds" not in added_cond_kwargs:
964
+ raise ValueError(
965
+ f"{self.__class__} has the config param `addition_embed_type` set to 'text_image' which requires the keyword argument `image_embeds` to be passed in `added_cond_kwargs`"
966
+ )
967
+
968
+ image_embs = added_cond_kwargs.get("image_embeds")
969
+ text_embs = added_cond_kwargs.get("text_embeds", encoder_hidden_states)
970
+ aug_emb = self.add_embedding(text_embs, image_embs)
971
+ elif self.config.addition_embed_type == "text_time":
972
+ # SDXL - style
973
+ if "text_embeds" not in added_cond_kwargs:
974
+ raise ValueError(
975
+ f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `text_embeds` to be passed in `added_cond_kwargs`"
976
+ )
977
+ text_embeds = added_cond_kwargs.get("text_embeds")
978
+ if "time_ids" not in added_cond_kwargs:
979
+ raise ValueError(
980
+ f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `time_ids` to be passed in `added_cond_kwargs`"
981
+ )
982
+ time_ids = added_cond_kwargs.get("time_ids")
983
+ time_embeds = self.add_time_proj(time_ids.flatten())
984
+ time_embeds = time_embeds.reshape((text_embeds.shape[0], -1))
985
+ add_embeds = torch.concat([text_embeds, time_embeds], dim=-1)
986
+ add_embeds = add_embeds.to(emb.dtype)
987
+ aug_emb = self.add_embedding(add_embeds)
988
+ elif self.config.addition_embed_type == "image":
989
+ # Kandinsky 2.2 - style
990
+ if "image_embeds" not in added_cond_kwargs:
991
+ raise ValueError(
992
+ f"{self.__class__} has the config param `addition_embed_type` set to 'image' which requires the keyword argument `image_embeds` to be passed in `added_cond_kwargs`"
993
+ )
994
+ image_embs = added_cond_kwargs.get("image_embeds")
995
+ aug_emb = self.add_embedding(image_embs)
996
+ elif self.config.addition_embed_type == "image_hint":
997
+ # Kandinsky 2.2 - style
998
+ if "image_embeds" not in added_cond_kwargs or "hint" not in added_cond_kwargs:
999
+ raise ValueError(
1000
+ f"{self.__class__} has the config param `addition_embed_type` set to 'image_hint' which requires the keyword arguments `image_embeds` and `hint` to be passed in `added_cond_kwargs`"
1001
+ )
1002
+ image_embs = added_cond_kwargs.get("image_embeds")
1003
+ hint = added_cond_kwargs.get("hint")
1004
+ aug_emb = self.add_embedding(image_embs, hint)
1005
+ return aug_emb
1006
+
1007
+ def process_encoder_hidden_states(
1008
+ self, encoder_hidden_states: torch.Tensor, added_cond_kwargs: Dict[str, Any]
1009
+ ) -> torch.Tensor:
1010
+ if self.encoder_hid_proj is not None and self.config.encoder_hid_dim_type == "text_proj":
1011
+ encoder_hidden_states = self.encoder_hid_proj(encoder_hidden_states)
1012
+ elif self.encoder_hid_proj is not None and self.config.encoder_hid_dim_type == "text_image_proj":
1013
+ # Kandinsky 2.1 - style
1014
+ if "image_embeds" not in added_cond_kwargs:
1015
+ raise ValueError(
1016
+ f"{self.__class__} has the config param `encoder_hid_dim_type` set to 'text_image_proj' which requires the keyword argument `image_embeds` to be passed in `added_conditions`"
1017
+ )
1018
+
1019
+ image_embeds = added_cond_kwargs.get("image_embeds")
1020
+ encoder_hidden_states = self.encoder_hid_proj(encoder_hidden_states, image_embeds)
1021
+ elif self.encoder_hid_proj is not None and self.config.encoder_hid_dim_type == "image_proj":
1022
+ # Kandinsky 2.2 - style
1023
+ if "image_embeds" not in added_cond_kwargs:
1024
+ raise ValueError(
1025
+ f"{self.__class__} has the config param `encoder_hid_dim_type` set to 'image_proj' which requires the keyword argument `image_embeds` to be passed in `added_conditions`"
1026
+ )
1027
+ image_embeds = added_cond_kwargs.get("image_embeds")
1028
+ encoder_hidden_states = self.encoder_hid_proj(image_embeds)
1029
+ elif self.encoder_hid_proj is not None and self.config.encoder_hid_dim_type == "ip_image_proj":
1030
+ if "image_embeds" not in added_cond_kwargs:
1031
+ raise ValueError(
1032
+ f"{self.__class__} has the config param `encoder_hid_dim_type` set to 'ip_image_proj' which requires the keyword argument `image_embeds` to be passed in `added_conditions`"
1033
+ )
1034
+ image_embeds = added_cond_kwargs.get("image_embeds")
1035
+ image_embeds = self.encoder_hid_proj(image_embeds)
1036
+ encoder_hidden_states = (encoder_hidden_states, image_embeds)
1037
+ return encoder_hidden_states
1038
+
1039
+ def forward(
1040
+ self,
1041
+ sample: torch.FloatTensor,
1042
+ timestep: Union[torch.Tensor, float, int],
1043
+ encoder_hidden_states: torch.Tensor,
1044
+ class_labels: Optional[torch.Tensor] = None,
1045
+ timestep_cond: Optional[torch.Tensor] = None,
1046
+ attention_mask: Optional[torch.Tensor] = None,
1047
+ cross_attention_kwargs: Optional[Dict[str, Any]] = None,
1048
+ added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None,
1049
+ down_block_additional_residuals: Optional[Tuple[torch.Tensor]] = None,
1050
+ mid_block_additional_residual: Optional[torch.Tensor] = None,
1051
+ down_intrablock_additional_residuals: Optional[Tuple[torch.Tensor]] = None,
1052
+ encoder_attention_mask: Optional[torch.Tensor] = None,
1053
+ return_dict: bool = True,
1054
+ down_block_add_samples: Optional[Tuple[torch.Tensor]] = None,
1055
+ mid_block_add_sample: Optional[Tuple[torch.Tensor]] = None,
1056
+ up_block_add_samples: Optional[Tuple[torch.Tensor]] = None,
1057
+ ) -> Union[UNet2DConditionOutput, Tuple]:
1058
+ r"""
1059
+ The [`UNet2DConditionModel`] forward method.
1060
+
1061
+ Args:
1062
+ sample (`torch.FloatTensor`):
1063
+ The noisy input tensor with the following shape `(batch, channel, height, width)`.
1064
+ timestep (`torch.FloatTensor` or `float` or `int`): The number of timesteps to denoise an input.
1065
+ encoder_hidden_states (`torch.FloatTensor`):
1066
+ The encoder hidden states with shape `(batch, sequence_length, feature_dim)`.
1067
+ class_labels (`torch.Tensor`, *optional*, defaults to `None`):
1068
+ Optional class labels for conditioning. Their embeddings will be summed with the timestep embeddings.
1069
+ timestep_cond: (`torch.Tensor`, *optional*, defaults to `None`):
1070
+ Conditional embeddings for timestep. If provided, the embeddings will be summed with the samples passed
1071
+ through the `self.time_embedding` layer to obtain the timestep embeddings.
1072
+ attention_mask (`torch.Tensor`, *optional*, defaults to `None`):
1073
+ An attention mask of shape `(batch, key_tokens)` is applied to `encoder_hidden_states`. If `1` the mask
1074
+ is kept, otherwise if `0` it is discarded. Mask will be converted into a bias, which adds large
1075
+ negative values to the attention scores corresponding to "discard" tokens.
1076
+ cross_attention_kwargs (`dict`, *optional*):
1077
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
1078
+ `self.processor` in
1079
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
1080
+ added_cond_kwargs: (`dict`, *optional*):
1081
+ A kwargs dictionary containing additional embeddings that if specified are added to the embeddings that
1082
+ are passed along to the UNet blocks.
1083
+ down_block_additional_residuals: (`tuple` of `torch.Tensor`, *optional*):
1084
+ A tuple of tensors that if specified are added to the residuals of down unet blocks.
1085
+ mid_block_additional_residual: (`torch.Tensor`, *optional*):
1086
+ A tensor that if specified is added to the residual of the middle unet block.
1087
+ down_intrablock_additional_residuals (`tuple` of `torch.Tensor`, *optional*):
1088
+ additional residuals to be added within UNet down blocks, for example from T2I-Adapter side model(s)
1089
+ encoder_attention_mask (`torch.Tensor`):
1090
+ A cross-attention mask of shape `(batch, sequence_length)` is applied to `encoder_hidden_states`. If
1091
+ `True` the mask is kept, otherwise if `False` it is discarded. Mask will be converted into a bias,
1092
+ which adds large negative values to the attention scores corresponding to "discard" tokens.
1093
+ return_dict (`bool`, *optional*, defaults to `True`):
1094
+ Whether or not to return a [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] instead of a plain
1095
+ tuple.
1096
+
1097
+ Returns:
1098
+ [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] or `tuple`:
1099
+ If `return_dict` is True, an [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] is returned,
1100
+ otherwise a `tuple` is returned where the first element is the sample tensor.
1101
+ """
1102
+ # By default samples have to be AT least a multiple of the overall upsampling factor.
1103
+ # The overall upsampling factor is equal to 2 ** (# num of upsampling layers).
1104
+ # However, the upsampling interpolation output size can be forced to fit any upsampling size
1105
+ # on the fly if necessary.
1106
+ default_overall_up_factor = 2 ** self.num_upsamplers
1107
+
1108
+ # upsample size should be forwarded when sample is not a multiple of `default_overall_up_factor`
1109
+ forward_upsample_size = False
1110
+ upsample_size = None
1111
+
1112
+ for dim in sample.shape[-2:]:
1113
+ if dim % default_overall_up_factor != 0:
1114
+ # Forward upsample size to force interpolation output size.
1115
+ forward_upsample_size = True
1116
+ break
1117
+
1118
+ # ensure attention_mask is a bias, and give it a singleton query_tokens dimension
1119
+ # expects mask of shape:
1120
+ # [batch, key_tokens]
1121
+ # adds singleton query_tokens dimension:
1122
+ # [batch, 1, key_tokens]
1123
+ # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes:
1124
+ # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn)
1125
+ # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn)
1126
+ if attention_mask is not None:
1127
+ # assume that mask is expressed as:
1128
+ # (1 = keep, 0 = discard)
1129
+ # convert mask into a bias that can be added to attention scores:
1130
+ # (keep = +0, discard = -10000.0)
1131
+ attention_mask = (1 - attention_mask.to(sample.dtype)) * -10000.0
1132
+ attention_mask = attention_mask.unsqueeze(1)
1133
+
1134
+ # convert encoder_attention_mask to a bias the same way we do for attention_mask
1135
+ if encoder_attention_mask is not None:
1136
+ encoder_attention_mask = (1 - encoder_attention_mask.to(sample.dtype)) * -10000.0
1137
+ encoder_attention_mask = encoder_attention_mask.unsqueeze(1)
1138
+
1139
+ # 0. center input if necessary
1140
+ if self.config.center_input_sample:
1141
+ sample = 2 * sample - 1.0
1142
+
1143
+ # 1. time
1144
+ t_emb = self.get_time_embed(sample=sample, timestep=timestep)
1145
+ emb = self.time_embedding(t_emb, timestep_cond)
1146
+ aug_emb = None
1147
+
1148
+ class_emb = self.get_class_embed(sample=sample, class_labels=class_labels)
1149
+ if class_emb is not None:
1150
+ if self.config.class_embeddings_concat:
1151
+ emb = torch.cat([emb, class_emb], dim=-1)
1152
+ else:
1153
+ emb = emb + class_emb
1154
+
1155
+ aug_emb = self.get_aug_embed(
1156
+ emb=emb, encoder_hidden_states=encoder_hidden_states, added_cond_kwargs=added_cond_kwargs
1157
+ )
1158
+ if self.config.addition_embed_type == "image_hint":
1159
+ aug_emb, hint = aug_emb
1160
+ sample = torch.cat([sample, hint], dim=1)
1161
+
1162
+ emb = emb + aug_emb if aug_emb is not None else emb
1163
+
1164
+ if self.time_embed_act is not None:
1165
+ emb = self.time_embed_act(emb)
1166
+
1167
+ encoder_hidden_states = self.process_encoder_hidden_states(
1168
+ encoder_hidden_states=encoder_hidden_states, added_cond_kwargs=added_cond_kwargs
1169
+ )
1170
+
1171
+ # 2. pre-process
1172
+ sample = self.conv_in(sample)
1173
+
1174
+ # 2.5 GLIGEN position net
1175
+ if cross_attention_kwargs is not None and cross_attention_kwargs.get("gligen", None) is not None:
1176
+ cross_attention_kwargs = cross_attention_kwargs.copy()
1177
+ gligen_args = cross_attention_kwargs.pop("gligen")
1178
+ cross_attention_kwargs["gligen"] = {"objs": self.position_net(**gligen_args)}
1179
+
1180
+ # 3. down
1181
+ # we're popping the `scale` instead of getting it because otherwise `scale` will be propagated
1182
+ # to the internal blocks and will raise deprecation warnings. this will be confusing for our users.
1183
+ if cross_attention_kwargs is not None:
1184
+ cross_attention_kwargs = cross_attention_kwargs.copy()
1185
+ lora_scale = cross_attention_kwargs.pop("scale", 1.0)
1186
+ else:
1187
+ lora_scale = 1.0
1188
+
1189
+ if USE_PEFT_BACKEND:
1190
+ # weight the lora layers by setting `lora_scale` for each PEFT layer
1191
+ scale_lora_layers(self, lora_scale)
1192
+
1193
+ is_controlnet = mid_block_additional_residual is not None and down_block_additional_residuals is not None
1194
+ # using new arg down_intrablock_additional_residuals for T2I-Adapters, to distinguish from controlnets
1195
+ is_adapter = down_intrablock_additional_residuals is not None
1196
+ # maintain backward compatibility for legacy usage, where
1197
+ # T2I-Adapter and ControlNet both use down_block_additional_residuals arg
1198
+ # but can only use one or the other
1199
+ is_brushnet = down_block_add_samples is not None and mid_block_add_sample is not None and up_block_add_samples is not None
1200
+ if not is_adapter and mid_block_additional_residual is None and down_block_additional_residuals is not None:
1201
+ deprecate(
1202
+ "T2I should not use down_block_additional_residuals",
1203
+ "1.3.0",
1204
+ "Passing intrablock residual connections with `down_block_additional_residuals` is deprecated \
1205
+ and will be removed in diffusers 1.3.0. `down_block_additional_residuals` should only be used \
1206
+ for ControlNet. Please make sure use `down_intrablock_additional_residuals` instead. ",
1207
+ standard_warn=False,
1208
+ )
1209
+ down_intrablock_additional_residuals = down_block_additional_residuals
1210
+ is_adapter = True
1211
+
1212
+ down_block_res_samples = (sample,)
1213
+
1214
+ if is_brushnet:
1215
+ sample = sample + down_block_add_samples.pop(0)
1216
+
1217
+ for downsample_block in self.down_blocks:
1218
+ if hasattr(downsample_block, "has_cross_attention") and downsample_block.has_cross_attention:
1219
+ # For t2i-adapter CrossAttnDownBlock2D
1220
+ additional_residuals = {}
1221
+ if is_adapter and len(down_intrablock_additional_residuals) > 0:
1222
+ additional_residuals["additional_residuals"] = down_intrablock_additional_residuals.pop(0)
1223
+
1224
+ i = len(down_block_add_samples)
1225
+
1226
+ if is_brushnet and len(down_block_add_samples) > 0:
1227
+ additional_residuals["down_block_add_samples"] = [down_block_add_samples.pop(0)
1228
+ for _ in range(
1229
+ len(downsample_block.resnets) + (downsample_block.downsamplers != None))]
1230
+
1231
+ sample, res_samples = downsample_block(
1232
+ hidden_states=sample,
1233
+ temb=emb,
1234
+ encoder_hidden_states=encoder_hidden_states,
1235
+ attention_mask=attention_mask,
1236
+ cross_attention_kwargs=cross_attention_kwargs,
1237
+ encoder_attention_mask=encoder_attention_mask,
1238
+ **additional_residuals,
1239
+ )
1240
+ else:
1241
+ additional_residuals = {}
1242
+
1243
+ i = len(down_block_add_samples)
1244
+
1245
+ if is_brushnet and len(down_block_add_samples) > 0:
1246
+ additional_residuals["down_block_add_samples"] = [down_block_add_samples.pop(0)
1247
+ for _ in range(
1248
+ len(downsample_block.resnets) + (downsample_block.downsamplers != None))]
1249
+
1250
+ sample, res_samples = downsample_block(hidden_states=sample, temb=emb, **additional_residuals)
1251
+ if is_adapter and len(down_intrablock_additional_residuals) > 0:
1252
+ sample += down_intrablock_additional_residuals.pop(0)
1253
+
1254
+ down_block_res_samples += res_samples
1255
+
1256
+ if is_controlnet:
1257
+ new_down_block_res_samples = ()
1258
+
1259
+ for down_block_res_sample, down_block_additional_residual in zip(
1260
+ down_block_res_samples, down_block_additional_residuals
1261
+ ):
1262
+ down_block_res_sample = down_block_res_sample + down_block_additional_residual
1263
+ new_down_block_res_samples = new_down_block_res_samples + (down_block_res_sample,)
1264
+
1265
+ down_block_res_samples = new_down_block_res_samples
1266
+
1267
+ # 4. mid
1268
+ if self.mid_block is not None:
1269
+ if hasattr(self.mid_block, "has_cross_attention") and self.mid_block.has_cross_attention:
1270
+ sample = self.mid_block(
1271
+ sample,
1272
+ emb,
1273
+ encoder_hidden_states=encoder_hidden_states,
1274
+ attention_mask=attention_mask,
1275
+ cross_attention_kwargs=cross_attention_kwargs,
1276
+ encoder_attention_mask=encoder_attention_mask,
1277
+ )
1278
+ else:
1279
+ sample = self.mid_block(sample, emb)
1280
+
1281
+ # To support T2I-Adapter-XL
1282
+ if (
1283
+ is_adapter
1284
+ and len(down_intrablock_additional_residuals) > 0
1285
+ and sample.shape == down_intrablock_additional_residuals[0].shape
1286
+ ):
1287
+ sample += down_intrablock_additional_residuals.pop(0)
1288
+
1289
+ if is_controlnet:
1290
+ sample = sample + mid_block_additional_residual
1291
+
1292
+ if is_brushnet:
1293
+ sample = sample + mid_block_add_sample
1294
+
1295
+ # 5. up
1296
+ for i, upsample_block in enumerate(self.up_blocks):
1297
+ is_final_block = i == len(self.up_blocks) - 1
1298
+
1299
+ res_samples = down_block_res_samples[-len(upsample_block.resnets):]
1300
+ down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)]
1301
+
1302
+ # if we have not reached the final block and need to forward the
1303
+ # upsample size, we do it here
1304
+ if not is_final_block and forward_upsample_size:
1305
+ upsample_size = down_block_res_samples[-1].shape[2:]
1306
+
1307
+ if hasattr(upsample_block, "has_cross_attention") and upsample_block.has_cross_attention:
1308
+ additional_residuals = {}
1309
+
1310
+ i = len(up_block_add_samples)
1311
+
1312
+ if is_brushnet and len(up_block_add_samples) > 0:
1313
+ additional_residuals["up_block_add_samples"] = [up_block_add_samples.pop(0)
1314
+ for _ in range(
1315
+ len(upsample_block.resnets) + (upsample_block.upsamplers != None))]
1316
+
1317
+ sample = upsample_block(
1318
+ hidden_states=sample,
1319
+ temb=emb,
1320
+ res_hidden_states_tuple=res_samples,
1321
+ encoder_hidden_states=encoder_hidden_states,
1322
+ cross_attention_kwargs=cross_attention_kwargs,
1323
+ upsample_size=upsample_size,
1324
+ attention_mask=attention_mask,
1325
+ encoder_attention_mask=encoder_attention_mask,
1326
+ **additional_residuals,
1327
+ )
1328
+ else:
1329
+ additional_residuals = {}
1330
+
1331
+ i = len(up_block_add_samples)
1332
+
1333
+ if is_brushnet and len(up_block_add_samples) > 0:
1334
+ additional_residuals["up_block_add_samples"] = [up_block_add_samples.pop(0)
1335
+ for _ in range(
1336
+ len(upsample_block.resnets) + (upsample_block.upsamplers != None))]
1337
+
1338
+ sample = upsample_block(
1339
+ hidden_states=sample,
1340
+ temb=emb,
1341
+ res_hidden_states_tuple=res_samples,
1342
+ upsample_size=upsample_size,
1343
+ **additional_residuals,
1344
+ )
1345
+
1346
+ # 6. post-process
1347
+ if self.conv_norm_out:
1348
+ sample = self.conv_norm_out(sample)
1349
+ sample = self.conv_act(sample)
1350
+ sample = self.conv_out(sample)
1351
+
1352
+ if USE_PEFT_BACKEND:
1353
+ # remove `lora_scale` from each PEFT layer
1354
+ unscale_lora_layers(self, lora_scale)
1355
+
1356
+ if not return_dict:
1357
+ return (sample,)
1358
+
1359
+ return UNet2DConditionOutput(sample=sample)
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ #credit to city96 for this module
2
+ #from https://github.com/city96/ComfyUI_ExtraModels/
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (170 Bytes). View file
 
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/config.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ List of all DiT model types / settings
3
+ """
4
+ sampling_settings = {
5
+ "beta_schedule" : "sqrt_linear",
6
+ "linear_start" : 0.0001,
7
+ "linear_end" : 0.02,
8
+ "timesteps" : 1000,
9
+ }
10
+
11
+ dit_conf = {
12
+ "XL/2": { # DiT_XL_2
13
+ "unet_config": {
14
+ "depth" : 28,
15
+ "num_heads" : 16,
16
+ "patch_size" : 2,
17
+ "hidden_size" : 1152,
18
+ },
19
+ "sampling_settings" : sampling_settings,
20
+ },
21
+ "XL/4": { # DiT_XL_4
22
+ "unet_config": {
23
+ "depth" : 28,
24
+ "num_heads" : 16,
25
+ "patch_size" : 4,
26
+ "hidden_size" : 1152,
27
+ },
28
+ "sampling_settings" : sampling_settings,
29
+ },
30
+ "XL/8": { # DiT_XL_8
31
+ "unet_config": {
32
+ "depth" : 28,
33
+ "num_heads" : 16,
34
+ "patch_size" : 8,
35
+ "hidden_size" : 1152,
36
+ },
37
+ "sampling_settings" : sampling_settings,
38
+ },
39
+ "L/2": { # DiT_L_2
40
+ "unet_config": {
41
+ "depth" : 24,
42
+ "num_heads" : 16,
43
+ "patch_size" : 2,
44
+ "hidden_size" : 1024,
45
+ },
46
+ "sampling_settings" : sampling_settings,
47
+ },
48
+ "L/4": { # DiT_L_4
49
+ "unet_config": {
50
+ "depth" : 24,
51
+ "num_heads" : 16,
52
+ "patch_size" : 4,
53
+ "hidden_size" : 1024,
54
+ },
55
+ "sampling_settings" : sampling_settings,
56
+ },
57
+ "L/8": { # DiT_L_8
58
+ "unet_config": {
59
+ "depth" : 24,
60
+ "num_heads" : 16,
61
+ "patch_size" : 8,
62
+ "hidden_size" : 1024,
63
+ },
64
+ "sampling_settings" : sampling_settings,
65
+ },
66
+ "B/2": { # DiT_B_2
67
+ "unet_config": {
68
+ "depth" : 12,
69
+ "num_heads" : 12,
70
+ "patch_size" : 2,
71
+ "hidden_size" : 768,
72
+ },
73
+ "sampling_settings" : sampling_settings,
74
+ },
75
+ "B/4": { # DiT_B_4
76
+ "unet_config": {
77
+ "depth" : 12,
78
+ "num_heads" : 12,
79
+ "patch_size" : 4,
80
+ "hidden_size" : 768,
81
+ },
82
+ "sampling_settings" : sampling_settings,
83
+ },
84
+ "B/8": { # DiT_B_8
85
+ "unet_config": {
86
+ "depth" : 12,
87
+ "num_heads" : 12,
88
+ "patch_size" : 8,
89
+ "hidden_size" : 768,
90
+ },
91
+ "sampling_settings" : sampling_settings,
92
+ },
93
+ "S/2": { # DiT_S_2
94
+ "unet_config": {
95
+ "depth" : 12,
96
+ "num_heads" : 6,
97
+ "patch_size" : 2,
98
+ "hidden_size" : 384,
99
+ },
100
+ "sampling_settings" : sampling_settings,
101
+ },
102
+ "S/4": { # DiT_S_4
103
+ "unet_config": {
104
+ "depth" : 12,
105
+ "num_heads" : 6,
106
+ "patch_size" : 4,
107
+ "hidden_size" : 384,
108
+ },
109
+ "sampling_settings" : sampling_settings,
110
+ },
111
+ "S/8": { # DiT_S_8
112
+ "unet_config": {
113
+ "depth" : 12,
114
+ "num_heads" : 6,
115
+ "patch_size" : 8,
116
+ "hidden_size" : 384,
117
+ },
118
+ "sampling_settings" : sampling_settings,
119
+ },
120
+ }
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/pixArt/LICENSE-Pixart ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU AFFERO GENERAL PUBLIC LICENSE
2
+ Version 3, 19 November 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU Affero General Public License is a free, copyleft license for
11
+ software and other kinds of works, specifically designed to ensure
12
+ cooperation with the community in the case of network server software.
13
+
14
+ The licenses for most software and other practical works are designed
15
+ to take away your freedom to share and change the works. By contrast,
16
+ our General Public Licenses are intended to guarantee your freedom to
17
+ share and change all versions of a program--to make sure it remains free
18
+ software for all its users.
19
+
20
+ When we speak of free software, we are referring to freedom, not
21
+ price. Our General Public Licenses are designed to make sure that you
22
+ have the freedom to distribute copies of free software (and charge for
23
+ them if you wish), that you receive source code or can get it if you
24
+ want it, that you can change the software or use pieces of it in new
25
+ free programs, and that you know you can do these things.
26
+
27
+ Developers that use our General Public Licenses protect your rights
28
+ with two steps: (1) assert copyright on the software, and (2) offer
29
+ you this License which gives you legal permission to copy, distribute
30
+ and/or modify the software.
31
+
32
+ A secondary benefit of defending all users' freedom is that
33
+ improvements made in alternate versions of the program, if they
34
+ receive widespread use, become available for other developers to
35
+ incorporate. Many developers of free software are heartened and
36
+ encouraged by the resulting cooperation. However, in the case of
37
+ software used on network servers, this result may fail to come about.
38
+ The GNU General Public License permits making a modified version and
39
+ letting the public access it on a server without ever releasing its
40
+ source code to the public.
41
+
42
+ The GNU Affero General Public License is designed specifically to
43
+ ensure that, in such cases, the modified source code becomes available
44
+ to the community. It requires the operator of a network server to
45
+ provide the source code of the modified version running there to the
46
+ users of that server. Therefore, public use of a modified version, on
47
+ a publicly accessible server, gives the public access to the source
48
+ code of the modified version.
49
+
50
+ An older license, called the Affero General Public License and
51
+ published by Affero, was designed to accomplish similar goals. This is
52
+ a different license, not a version of the Affero GPL, but Affero has
53
+ released a new version of the Affero GPL which permits relicensing under
54
+ this license.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ TERMS AND CONDITIONS
60
+
61
+ 0. Definitions.
62
+
63
+ "This License" refers to version 3 of the GNU Affero General Public License.
64
+
65
+ "Copyright" also means copyright-like laws that apply to other kinds of
66
+ works, such as semiconductor masks.
67
+
68
+ "The Program" refers to any copyrightable work licensed under this
69
+ License. Each licensee is addressed as "you". "Licensees" and
70
+ "recipients" may be individuals or organizations.
71
+
72
+ To "modify" a work means to copy from or adapt all or part of the work
73
+ in a fashion requiring copyright permission, other than the making of an
74
+ exact copy. The resulting work is called a "modified version" of the
75
+ earlier work or a work "based on" the earlier work.
76
+
77
+ A "covered work" means either the unmodified Program or a work based
78
+ on the Program.
79
+
80
+ To "propagate" a work means to do anything with it that, without
81
+ permission, would make you directly or secondarily liable for
82
+ infringement under applicable copyright law, except executing it on a
83
+ computer or modifying a private copy. Propagation includes copying,
84
+ distribution (with or without modification), making available to the
85
+ public, and in some countries other activities as well.
86
+
87
+ To "convey" a work means any kind of propagation that enables other
88
+ parties to make or receive copies. Mere interaction with a user through
89
+ a computer network, with no transfer of a copy, is not conveying.
90
+
91
+ An interactive user interface displays "Appropriate Legal Notices"
92
+ to the extent that it includes a convenient and prominently visible
93
+ feature that (1) displays an appropriate copyright notice, and (2)
94
+ tells the user that there is no warranty for the work (except to the
95
+ extent that warranties are provided), that licensees may convey the
96
+ work under this License, and how to view a copy of this License. If
97
+ the interface presents a list of user commands or options, such as a
98
+ menu, a prominent item in the list meets this criterion.
99
+
100
+ 1. Source Code.
101
+
102
+ The "source code" for a work means the preferred form of the work
103
+ for making modifications to it. "Object code" means any non-source
104
+ form of a work.
105
+
106
+ A "Standard Interface" means an interface that either is an official
107
+ standard defined by a recognized standards body, or, in the case of
108
+ interfaces specified for a particular programming language, one that
109
+ is widely used among developers working in that language.
110
+
111
+ The "System Libraries" of an executable work include anything, other
112
+ than the work as a whole, that (a) is included in the normal form of
113
+ packaging a Major Component, but which is not part of that Major
114
+ Component, and (b) serves only to enable use of the work with that
115
+ Major Component, or to implement a Standard Interface for which an
116
+ implementation is available to the public in source code form. A
117
+ "Major Component", in this context, means a major essential component
118
+ (kernel, window system, and so on) of the specific operating system
119
+ (if any) on which the executable work runs, or a compiler used to
120
+ produce the work, or an object code interpreter used to run it.
121
+
122
+ The "Corresponding Source" for a work in object code form means all
123
+ the source code needed to generate, install, and (for an executable
124
+ work) run the object code and to modify the work, including scripts to
125
+ control those activities. However, it does not include the work's
126
+ System Libraries, or general-purpose tools or generally available free
127
+ programs which are used unmodified in performing those activities but
128
+ which are not part of the work. For example, Corresponding Source
129
+ includes interface definition files associated with source files for
130
+ the work, and the source code for shared libraries and dynamically
131
+ linked subprograms that the work is specifically designed to require,
132
+ such as by intimate data communication or control flow between those
133
+ subprograms and other parts of the work.
134
+
135
+ The Corresponding Source need not include anything that users
136
+ can regenerate automatically from other parts of the Corresponding
137
+ Source.
138
+
139
+ The Corresponding Source for a work in source code form is that
140
+ same work.
141
+
142
+ 2. Basic Permissions.
143
+
144
+ All rights granted under this License are granted for the term of
145
+ copyright on the Program, and are irrevocable provided the stated
146
+ conditions are met. This License explicitly affirms your unlimited
147
+ permission to run the unmodified Program. The output from running a
148
+ covered work is covered by this License only if the output, given its
149
+ content, constitutes a covered work. This License acknowledges your
150
+ rights of fair use or other equivalent, as provided by copyright law.
151
+
152
+ You may make, run and propagate covered works that you do not
153
+ convey, without conditions so long as your license otherwise remains
154
+ in force. You may convey covered works to others for the sole purpose
155
+ of having them make modifications exclusively for you, or provide you
156
+ with facilities for running those works, provided that you comply with
157
+ the terms of this License in conveying all material for which you do
158
+ not control copyright. Those thus making or running the covered works
159
+ for you must do so exclusively on your behalf, under your direction
160
+ and control, on terms that prohibit them from making any copies of
161
+ your copyrighted material outside their relationship with you.
162
+
163
+ Conveying under any other circumstances is permitted solely under
164
+ the conditions stated below. Sublicensing is not allowed; section 10
165
+ makes it unnecessary.
166
+
167
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168
+
169
+ No covered work shall be deemed part of an effective technological
170
+ measure under any applicable law fulfilling obligations under article
171
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172
+ similar laws prohibiting or restricting circumvention of such
173
+ measures.
174
+
175
+ When you convey a covered work, you waive any legal power to forbid
176
+ circumvention of technological measures to the extent such circumvention
177
+ is effected by exercising rights under this License with respect to
178
+ the covered work, and you disclaim any intention to limit operation or
179
+ modification of the work as a means of enforcing, against the work's
180
+ users, your or third parties' legal rights to forbid circumvention of
181
+ technological measures.
182
+
183
+ 4. Conveying Verbatim Copies.
184
+
185
+ You may convey verbatim copies of the Program's source code as you
186
+ receive it, in any medium, provided that you conspicuously and
187
+ appropriately publish on each copy an appropriate copyright notice;
188
+ keep intact all notices stating that this License and any
189
+ non-permissive terms added in accord with section 7 apply to the code;
190
+ keep intact all notices of the absence of any warranty; and give all
191
+ recipients a copy of this License along with the Program.
192
+
193
+ You may charge any price or no price for each copy that you convey,
194
+ and you may offer support or warranty protection for a fee.
195
+
196
+ 5. Conveying Modified Source Versions.
197
+
198
+ You may convey a work based on the Program, or the modifications to
199
+ produce it from the Program, in the form of source code under the
200
+ terms of section 4, provided that you also meet all of these conditions:
201
+
202
+ a) The work must carry prominent notices stating that you modified
203
+ it, and giving a relevant date.
204
+
205
+ b) The work must carry prominent notices stating that it is
206
+ released under this License and any conditions added under section
207
+ 7. This requirement modifies the requirement in section 4 to
208
+ "keep intact all notices".
209
+
210
+ c) You must license the entire work, as a whole, under this
211
+ License to anyone who comes into possession of a copy. This
212
+ License will therefore apply, along with any applicable section 7
213
+ additional terms, to the whole of the work, and all its parts,
214
+ regardless of how they are packaged. This License gives no
215
+ permission to license the work in any other way, but it does not
216
+ invalidate such permission if you have separately received it.
217
+
218
+ d) If the work has interactive user interfaces, each must display
219
+ Appropriate Legal Notices; however, if the Program has interactive
220
+ interfaces that do not display Appropriate Legal Notices, your
221
+ work need not make them do so.
222
+
223
+ A compilation of a covered work with other separate and independent
224
+ works, which are not by their nature extensions of the covered work,
225
+ and which are not combined with it such as to form a larger program,
226
+ in or on a volume of a storage or distribution medium, is called an
227
+ "aggregate" if the compilation and its resulting copyright are not
228
+ used to limit the access or legal rights of the compilation's users
229
+ beyond what the individual works permit. Inclusion of a covered work
230
+ in an aggregate does not cause this License to apply to the other
231
+ parts of the aggregate.
232
+
233
+ 6. Conveying Non-Source Forms.
234
+
235
+ You may convey a covered work in object code form under the terms
236
+ of sections 4 and 5, provided that you also convey the
237
+ machine-readable Corresponding Source under the terms of this License,
238
+ in one of these ways:
239
+
240
+ a) Convey the object code in, or embodied in, a physical product
241
+ (including a physical distribution medium), accompanied by the
242
+ Corresponding Source fixed on a durable physical medium
243
+ customarily used for software interchange.
244
+
245
+ b) Convey the object code in, or embodied in, a physical product
246
+ (including a physical distribution medium), accompanied by a
247
+ written offer, valid for at least three years and valid for as
248
+ long as you offer spare parts or customer support for that product
249
+ model, to give anyone who possesses the object code either (1) a
250
+ copy of the Corresponding Source for all the software in the
251
+ product that is covered by this License, on a durable physical
252
+ medium customarily used for software interchange, for a price no
253
+ more than your reasonable cost of physically performing this
254
+ conveying of source, or (2) access to copy the
255
+ Corresponding Source from a network server at no charge.
256
+
257
+ c) Convey individual copies of the object code with a copy of the
258
+ written offer to provide the Corresponding Source. This
259
+ alternative is allowed only occasionally and noncommercially, and
260
+ only if you received the object code with such an offer, in accord
261
+ with subsection 6b.
262
+
263
+ d) Convey the object code by offering access from a designated
264
+ place (gratis or for a charge), and offer equivalent access to the
265
+ Corresponding Source in the same way through the same place at no
266
+ further charge. You need not require recipients to copy the
267
+ Corresponding Source along with the object code. If the place to
268
+ copy the object code is a network server, the Corresponding Source
269
+ may be on a different server (operated by you or a third party)
270
+ that supports equivalent copying facilities, provided you maintain
271
+ clear directions next to the object code saying where to find the
272
+ Corresponding Source. Regardless of what server hosts the
273
+ Corresponding Source, you remain obligated to ensure that it is
274
+ available for as long as needed to satisfy these requirements.
275
+
276
+ e) Convey the object code using peer-to-peer transmission, provided
277
+ you inform other peers where the object code and Corresponding
278
+ Source of the work are being offered to the general public at no
279
+ charge under subsection 6d.
280
+
281
+ A separable portion of the object code, whose source code is excluded
282
+ from the Corresponding Source as a System Library, need not be
283
+ included in conveying the object code work.
284
+
285
+ A "User Product" is either (1) a "consumer product", which means any
286
+ tangible personal property which is normally used for personal, family,
287
+ or household purposes, or (2) anything designed or sold for incorporation
288
+ into a dwelling. In determining whether a product is a consumer product,
289
+ doubtful cases shall be resolved in favor of coverage. For a particular
290
+ product received by a particular user, "normally used" refers to a
291
+ typical or common use of that class of product, regardless of the status
292
+ of the particular user or of the way in which the particular user
293
+ actually uses, or expects or is expected to use, the product. A product
294
+ is a consumer product regardless of whether the product has substantial
295
+ commercial, industrial or non-consumer uses, unless such uses represent
296
+ the only significant mode of use of the product.
297
+
298
+ "Installation Information" for a User Product means any methods,
299
+ procedures, authorization keys, or other information required to install
300
+ and execute modified versions of a covered work in that User Product from
301
+ a modified version of its Corresponding Source. The information must
302
+ suffice to ensure that the continued functioning of the modified object
303
+ code is in no case prevented or interfered with solely because
304
+ modification has been made.
305
+
306
+ If you convey an object code work under this section in, or with, or
307
+ specifically for use in, a User Product, and the conveying occurs as
308
+ part of a transaction in which the right of possession and use of the
309
+ User Product is transferred to the recipient in perpetuity or for a
310
+ fixed term (regardless of how the transaction is characterized), the
311
+ Corresponding Source conveyed under this section must be accompanied
312
+ by the Installation Information. But this requirement does not apply
313
+ if neither you nor any third party retains the ability to install
314
+ modified object code on the User Product (for example, the work has
315
+ been installed in ROM).
316
+
317
+ The requirement to provide Installation Information does not include a
318
+ requirement to continue to provide support service, warranty, or updates
319
+ for a work that has been modified or installed by the recipient, or for
320
+ the User Product in which it has been modified or installed. Access to a
321
+ network may be denied when the modification itself materially and
322
+ adversely affects the operation of the network or violates the rules and
323
+ protocols for communication across the network.
324
+
325
+ Corresponding Source conveyed, and Installation Information provided,
326
+ in accord with this section must be in a format that is publicly
327
+ documented (and with an implementation available to the public in
328
+ source code form), and must require no special password or key for
329
+ unpacking, reading or copying.
330
+
331
+ 7. Additional Terms.
332
+
333
+ "Additional permissions" are terms that supplement the terms of this
334
+ License by making exceptions from one or more of its conditions.
335
+ Additional permissions that are applicable to the entire Program shall
336
+ be treated as though they were included in this License, to the extent
337
+ that they are valid under applicable law. If additional permissions
338
+ apply only to part of the Program, that part may be used separately
339
+ under those permissions, but the entire Program remains governed by
340
+ this License without regard to the additional permissions.
341
+
342
+ When you convey a copy of a covered work, you may at your option
343
+ remove any additional permissions from that copy, or from any part of
344
+ it. (Additional permissions may be written to require their own
345
+ removal in certain cases when you modify the work.) You may place
346
+ additional permissions on material, added by you to a covered work,
347
+ for which you have or can give appropriate copyright permission.
348
+
349
+ Notwithstanding any other provision of this License, for material you
350
+ add to a covered work, you may (if authorized by the copyright holders of
351
+ that material) supplement the terms of this License with terms:
352
+
353
+ a) Disclaiming warranty or limiting liability differently from the
354
+ terms of sections 15 and 16 of this License; or
355
+
356
+ b) Requiring preservation of specified reasonable legal notices or
357
+ author attributions in that material or in the Appropriate Legal
358
+ Notices displayed by works containing it; or
359
+
360
+ c) Prohibiting misrepresentation of the origin of that material, or
361
+ requiring that modified versions of such material be marked in
362
+ reasonable ways as different from the original version; or
363
+
364
+ d) Limiting the use for publicity purposes of names of licensors or
365
+ authors of the material; or
366
+
367
+ e) Declining to grant rights under trademark law for use of some
368
+ trade names, trademarks, or service marks; or
369
+
370
+ f) Requiring indemnification of licensors and authors of that
371
+ material by anyone who conveys the material (or modified versions of
372
+ it) with contractual assumptions of liability to the recipient, for
373
+ any liability that these contractual assumptions directly impose on
374
+ those licensors and authors.
375
+
376
+ All other non-permissive additional terms are considered "further
377
+ restrictions" within the meaning of section 10. If the Program as you
378
+ received it, or any part of it, contains a notice stating that it is
379
+ governed by this License along with a term that is a further
380
+ restriction, you may remove that term. If a license document contains
381
+ a further restriction but permits relicensing or conveying under this
382
+ License, you may add to a covered work material governed by the terms
383
+ of that license document, provided that the further restriction does
384
+ not survive such relicensing or conveying.
385
+
386
+ If you add terms to a covered work in accord with this section, you
387
+ must place, in the relevant source files, a statement of the
388
+ additional terms that apply to those files, or a notice indicating
389
+ where to find the applicable terms.
390
+
391
+ Additional terms, permissive or non-permissive, may be stated in the
392
+ form of a separately written license, or stated as exceptions;
393
+ the above requirements apply either way.
394
+
395
+ 8. Termination.
396
+
397
+ You may not propagate or modify a covered work except as expressly
398
+ provided under this License. Any attempt otherwise to propagate or
399
+ modify it is void, and will automatically terminate your rights under
400
+ this License (including any patent licenses granted under the third
401
+ paragraph of section 11).
402
+
403
+ However, if you cease all violation of this License, then your
404
+ license from a particular copyright holder is reinstated (a)
405
+ provisionally, unless and until the copyright holder explicitly and
406
+ finally terminates your license, and (b) permanently, if the copyright
407
+ holder fails to notify you of the violation by some reasonable means
408
+ prior to 60 days after the cessation.
409
+
410
+ Moreover, your license from a particular copyright holder is
411
+ reinstated permanently if the copyright holder notifies you of the
412
+ violation by some reasonable means, this is the first time you have
413
+ received notice of violation of this License (for any work) from that
414
+ copyright holder, and you cure the violation prior to 30 days after
415
+ your receipt of the notice.
416
+
417
+ Termination of your rights under this section does not terminate the
418
+ licenses of parties who have received copies or rights from you under
419
+ this License. If your rights have been terminated and not permanently
420
+ reinstated, you do not qualify to receive new licenses for the same
421
+ material under section 10.
422
+
423
+ 9. Acceptance Not Required for Having Copies.
424
+
425
+ You are not required to accept this License in order to receive or
426
+ run a copy of the Program. Ancillary propagation of a covered work
427
+ occurring solely as a consequence of using peer-to-peer transmission
428
+ to receive a copy likewise does not require acceptance. However,
429
+ nothing other than this License grants you permission to propagate or
430
+ modify any covered work. These actions infringe copyright if you do
431
+ not accept this License. Therefore, by modifying or propagating a
432
+ covered work, you indicate your acceptance of this License to do so.
433
+
434
+ 10. Automatic Licensing of Downstream Recipients.
435
+
436
+ Each time you convey a covered work, the recipient automatically
437
+ receives a license from the original licensors, to run, modify and
438
+ propagate that work, subject to this License. You are not responsible
439
+ for enforcing compliance by third parties with this License.
440
+
441
+ An "entity transaction" is a transaction transferring control of an
442
+ organization, or substantially all assets of one, or subdividing an
443
+ organization, or merging organizations. If propagation of a covered
444
+ work results from an entity transaction, each party to that
445
+ transaction who receives a copy of the work also receives whatever
446
+ licenses to the work the party's predecessor in interest had or could
447
+ give under the previous paragraph, plus a right to possession of the
448
+ Corresponding Source of the work from the predecessor in interest, if
449
+ the predecessor has it or can get it with reasonable efforts.
450
+
451
+ You may not impose any further restrictions on the exercise of the
452
+ rights granted or affirmed under this License. For example, you may
453
+ not impose a license fee, royalty, or other charge for exercise of
454
+ rights granted under this License, and you may not initiate litigation
455
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
456
+ any patent claim is infringed by making, using, selling, offering for
457
+ sale, or importing the Program or any portion of it.
458
+
459
+ 11. Patents.
460
+
461
+ A "contributor" is a copyright holder who authorizes use under this
462
+ License of the Program or a work on which the Program is based. The
463
+ work thus licensed is called the contributor's "contributor version".
464
+
465
+ A contributor's "essential patent claims" are all patent claims
466
+ owned or controlled by the contributor, whether already acquired or
467
+ hereafter acquired, that would be infringed by some manner, permitted
468
+ by this License, of making, using, or selling its contributor version,
469
+ but do not include claims that would be infringed only as a
470
+ consequence of further modification of the contributor version. For
471
+ purposes of this definition, "control" includes the right to grant
472
+ patent sublicenses in a manner consistent with the requirements of
473
+ this License.
474
+
475
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
476
+ patent license under the contributor's essential patent claims, to
477
+ make, use, sell, offer for sale, import and otherwise run, modify and
478
+ propagate the contents of its contributor version.
479
+
480
+ In the following three paragraphs, a "patent license" is any express
481
+ agreement or commitment, however denominated, not to enforce a patent
482
+ (such as an express permission to practice a patent or covenant not to
483
+ sue for patent infringement). To "grant" such a patent license to a
484
+ party means to make such an agreement or commitment not to enforce a
485
+ patent against the party.
486
+
487
+ If you convey a covered work, knowingly relying on a patent license,
488
+ and the Corresponding Source of the work is not available for anyone
489
+ to copy, free of charge and under the terms of this License, through a
490
+ publicly available network server or other readily accessible means,
491
+ then you must either (1) cause the Corresponding Source to be so
492
+ available, or (2) arrange to deprive yourself of the benefit of the
493
+ patent license for this particular work, or (3) arrange, in a manner
494
+ consistent with the requirements of this License, to extend the patent
495
+ license to downstream recipients. "Knowingly relying" means you have
496
+ actual knowledge that, but for the patent license, your conveying the
497
+ covered work in a country, or your recipient's use of the covered work
498
+ in a country, would infringe one or more identifiable patents in that
499
+ country that you have reason to believe are valid.
500
+
501
+ If, pursuant to or in connection with a single transaction or
502
+ arrangement, you convey, or propagate by procuring conveyance of, a
503
+ covered work, and grant a patent license to some of the parties
504
+ receiving the covered work authorizing them to use, propagate, modify
505
+ or convey a specific copy of the covered work, then the patent license
506
+ you grant is automatically extended to all recipients of the covered
507
+ work and works based on it.
508
+
509
+ A patent license is "discriminatory" if it does not include within
510
+ the scope of its coverage, prohibits the exercise of, or is
511
+ conditioned on the non-exercise of one or more of the rights that are
512
+ specifically granted under this License. You may not convey a covered
513
+ work if you are a party to an arrangement with a third party that is
514
+ in the business of distributing software, under which you make payment
515
+ to the third party based on the extent of your activity of conveying
516
+ the work, and under which the third party grants, to any of the
517
+ parties who would receive the covered work from you, a discriminatory
518
+ patent license (a) in connection with copies of the covered work
519
+ conveyed by you (or copies made from those copies), or (b) primarily
520
+ for and in connection with specific products or compilations that
521
+ contain the covered work, unless you entered into that arrangement,
522
+ or that patent license was granted, prior to 28 March 2007.
523
+
524
+ Nothing in this License shall be construed as excluding or limiting
525
+ any implied license or other defenses to infringement that may
526
+ otherwise be available to you under applicable patent law.
527
+
528
+ 12. No Surrender of Others' Freedom.
529
+
530
+ If conditions are imposed on you (whether by court order, agreement or
531
+ otherwise) that contradict the conditions of this License, they do not
532
+ excuse you from the conditions of this License. If you cannot convey a
533
+ covered work so as to satisfy simultaneously your obligations under this
534
+ License and any other pertinent obligations, then as a consequence you may
535
+ not convey it at all. For example, if you agree to terms that obligate you
536
+ to collect a royalty for further conveying from those to whom you convey
537
+ the Program, the only way you could satisfy both those terms and this
538
+ License would be to refrain entirely from conveying the Program.
539
+
540
+ 13. Remote Network Interaction; Use with the GNU General Public License.
541
+
542
+ Notwithstanding any other provision of this License, if you modify the
543
+ Program, your modified version must prominently offer all users
544
+ interacting with it remotely through a computer network (if your version
545
+ supports such interaction) an opportunity to receive the Corresponding
546
+ Source of your version by providing access to the Corresponding Source
547
+ from a network server at no charge, through some standard or customary
548
+ means of facilitating copying of software. This Corresponding Source
549
+ shall include the Corresponding Source for any work covered by version 3
550
+ of the GNU General Public License that is incorporated pursuant to the
551
+ following paragraph.
552
+
553
+ Notwithstanding any other provision of this License, you have
554
+ permission to link or combine any covered work with a work licensed
555
+ under version 3 of the GNU General Public License into a single
556
+ combined work, and to convey the resulting work. The terms of this
557
+ License will continue to apply to the part which is the covered work,
558
+ but the work with which it is combined will remain governed by version
559
+ 3 of the GNU General Public License.
560
+
561
+ 14. Revised Versions of this License.
562
+
563
+ The Free Software Foundation may publish revised and/or new versions of
564
+ the GNU Affero General Public License from time to time. Such new versions
565
+ will be similar in spirit to the present version, but may differ in detail to
566
+ address new problems or concerns.
567
+
568
+ Each version is given a distinguishing version number. If the
569
+ Program specifies that a certain numbered version of the GNU Affero General
570
+ Public License "or any later version" applies to it, you have the
571
+ option of following the terms and conditions either of that numbered
572
+ version or of any later version published by the Free Software
573
+ Foundation. If the Program does not specify a version number of the
574
+ GNU Affero General Public License, you may choose any version ever published
575
+ by the Free Software Foundation.
576
+
577
+ If the Program specifies that a proxy can decide which future
578
+ versions of the GNU Affero General Public License can be used, that proxy's
579
+ public statement of acceptance of a version permanently authorizes you
580
+ to choose that version for the Program.
581
+
582
+ Later license versions may give you additional or different
583
+ permissions. However, no additional obligations are imposed on any
584
+ author or copyright holder as a result of your choosing to follow a
585
+ later version.
586
+
587
+ 15. Disclaimer of Warranty.
588
+
589
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597
+
598
+ 16. Limitation of Liability.
599
+
600
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608
+ SUCH DAMAGES.
609
+
610
+ 17. Interpretation of Sections 15 and 16.
611
+
612
+ If the disclaimer of warranty and limitation of liability provided
613
+ above cannot be given local legal effect according to their terms,
614
+ reviewing courts shall apply local law that most closely approximates
615
+ an absolute waiver of all civil liability in connection with the
616
+ Program, unless a warranty or assumption of liability accompanies a
617
+ copy of the Program in return for a fee.
618
+
619
+ END OF TERMS AND CONDITIONS
620
+
621
+ How to Apply These Terms to Your New Programs
622
+
623
+ If you develop a new program, and you want it to be of the greatest
624
+ possible use to the public, the best way to achieve this is to make it
625
+ free software which everyone can redistribute and change under these terms.
626
+
627
+ To do so, attach the following notices to the program. It is safest
628
+ to attach them to the start of each source file to most effectively
629
+ state the exclusion of warranty; and each file should have at least
630
+ the "copyright" line and a pointer to where the full notice is found.
631
+
632
+ <one line to give the program's name and a brief idea of what it does.>
633
+ Copyright (C) <year> <name of author>
634
+
635
+ This program is free software: you can redistribute it and/or modify
636
+ it under the terms of the GNU Affero General Public License as published
637
+ by the Free Software Foundation, either version 3 of the License, or
638
+ (at your option) any later version.
639
+
640
+ This program is distributed in the hope that it will be useful,
641
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
642
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643
+ GNU Affero General Public License for more details.
644
+
645
+ You should have received a copy of the GNU Affero General Public License
646
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
647
+
648
+ Also add information on how to contact you by electronic and paper mail.
649
+
650
+ If your software can interact with users remotely through a computer
651
+ network, you should also make sure that it provides a way for users to
652
+ get its source. For example, if your program is a web application, its
653
+ interface could display a "Source" link that leads users to an archive
654
+ of the code. There are many ways you could offer source, and different
655
+ solutions will be better for different programs; see section 13 for the
656
+ specific requirements.
657
+
658
+ You should also get your employer (if you work as a programmer) or school,
659
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
660
+ For more information on this, and how to apply and follow the GNU AGPL, see
661
+ <https://www.gnu.org/licenses/>.
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/pixArt/__init__.py ADDED
File without changes
ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/pixArt/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (177 Bytes). View file