Upload folder using huggingface_hub (part 6)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +25 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/api/bizyair.py +372 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/api/fluxai.py +51 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/api/stability.py +200 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/cache.py +87 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/chooser.py +153 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/colorfix.py +115 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/conditioning.py +57 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/controlnet.py +93 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/dynthres_core.py +167 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/easing.py +27 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/gradual_latent_hires_fix.py +273 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/image.py +227 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/lllite.py +237 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/loader.py +570 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/log.py +77 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/math.py +148 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/messages.py +55 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/model.py +58 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/sampler.py +1053 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/styleAlign.py +148 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/translate.py +247 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/utils.py +300 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/wildcards.py +476 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/libs/xyplot.py +719 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/ben/model.py +1310 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/bitsandbytes_NF4/__init__.py +167 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/briaai/__init__.py +0 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/briaai/rembg.py +475 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__init__.py +822 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/__init__.cpython-312.pyc +0 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/model.cpython-312.pyc +0 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/model_patch.cpython-312.pyc +0 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/powerpaint_utils.cpython-312.pyc +0 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/unet_2d_blocks.cpython-312.pyc +3 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/__pycache__/unet_2d_condition.cpython-312.pyc +0 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/config/brushnet.json +58 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/config/brushnet_xl.json +63 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/config/powerpaint.json +57 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/model.py +1688 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/model_patch.py +137 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/powerpaint_utils.py +467 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/unet_2d_blocks.py +0 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/brushnet/unet_2d_condition.py +1359 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/__init__.py +2 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/__pycache__/__init__.cpython-312.pyc +0 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/config.py +120 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/pixArt/LICENSE-Pixart +661 -0
- ComfyUI/custom_nodes/ComfyUI-Easy-Use/py/modules/dit/pixArt/__init__.py +0 -0
- 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
|
|
|