Compare commits
147
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2918d4b07d | ||
|
|
c40e49be46 | ||
|
|
09957617d3 | ||
|
|
c703aa7058 | ||
|
|
64d366d323 | ||
|
|
333e0a2faa | ||
|
|
a677d95bc8 | ||
|
|
aafd87e84b | ||
|
|
efa3bae54b | ||
|
|
8e4362689d | ||
|
|
b86634284e | ||
|
|
b3293ddccd | ||
|
|
4b40831b83 | ||
|
|
e426b0521c | ||
|
|
1777bf6e06 | ||
|
|
dafc892f0f | ||
|
|
fea0cfd5dd | ||
|
|
a7e158db6d | ||
|
|
455ac4abd3 | ||
|
|
778dfa2cf5 | ||
|
|
329f2e6f81 | ||
|
|
63ad6d97d7 | ||
|
|
8db56db7cf | ||
|
|
bd542f1e0b | ||
|
|
a162e53dea | ||
|
|
a8a4c848ed | ||
|
|
bddd38996a | ||
|
|
9f084eae94 | ||
|
|
c54c635161 | ||
|
|
fa8b42e05e | ||
|
|
9c3c323884 | ||
|
|
c8a46439be | ||
|
|
b7ec701259 | ||
|
|
a2cd0e0a38 | ||
|
|
7bccc0e236 | ||
|
|
a3a649a79f | ||
|
|
2a14d30552 | ||
|
|
313bef0609 | ||
|
|
960a80aeca | ||
|
|
20e6d50a98 | ||
|
|
507c3417d6 | ||
|
|
d1adb8d4ed | ||
|
|
915ff12747 | ||
|
|
fbade79137 | ||
|
|
a240a677d0 | ||
|
|
ab64cf31f6 | ||
|
|
67f2e32dae | ||
|
|
0dc40fe052 | ||
|
|
b7225be552 | ||
|
|
629e00ec94 | ||
|
|
6d033c9314 | ||
|
|
4f8926ed00 | ||
|
|
1daa1a4603 | ||
|
|
062773d929 | ||
|
|
57decadaef | ||
|
|
ec804ab7c9 | ||
|
|
3ee7533098 | ||
|
|
0d383ccc1f | ||
|
|
e2f2257c34 | ||
|
|
d62b9fc4c6 | ||
|
|
be7ad0c7fb | ||
|
|
156864cc8b | ||
|
|
7240e496cc | ||
|
|
8acdf4018d | ||
|
|
1ea7c3e203 | ||
|
|
e54aeb6125 | ||
|
|
d59f51fbcf | ||
|
|
a667eb6982 | ||
|
|
8d72732247 | ||
|
|
f0f3b30a62 | ||
|
|
d99fe24542 | ||
|
|
ea4c7381bd | ||
|
|
e900d20641 | ||
|
|
8a46647d8c | ||
|
|
968178bf57 | ||
|
|
406a255db0 | ||
|
|
574557810e | ||
|
|
998a02c3a4 | ||
|
|
c6f964c921 | ||
|
|
efb0e147c5 | ||
|
|
9cf7356f98 | ||
|
|
af05c43174 | ||
|
|
380c68ff2b | ||
|
|
068b00b99f | ||
|
|
cd6a42ab64 | ||
|
|
f115abec92 | ||
|
|
f0e23cf878 | ||
|
|
a94f11d809 | ||
|
|
38972bea5f | ||
|
|
a761ff552a | ||
|
|
dbeb84ea9a | ||
|
|
0a4938f39a | ||
|
|
b2182c716d | ||
|
|
8253be73f6 | ||
|
|
20318e296e | ||
|
|
cea1b69286 | ||
|
|
ab8aa69389 | ||
|
|
681491f1d0 | ||
|
|
c2fb815074 | ||
|
|
fffa14dc44 | ||
|
|
df37166d42 | ||
|
|
25fa3a8f6a | ||
|
|
b11507c5e6 | ||
|
|
f06d02489f | ||
|
|
c203af2f71 | ||
|
|
c715155a70 | ||
|
|
8163133294 | ||
|
|
765be5dab4 | ||
|
|
7c1523389d | ||
|
|
7a2b1ba166 | ||
|
|
45b4dcfcd0 | ||
|
|
3b2e535566 | ||
|
|
db556d13a3 | ||
|
|
a987063c68 | ||
|
|
4ce30ef899 | ||
|
|
d988282d98 | ||
|
|
695fdf7ceb | ||
|
|
0befe164cc | ||
|
|
d506c68a80 | ||
|
|
c59c429b75 | ||
|
|
c726b6e4a2 | ||
|
|
96075ad4e1 | ||
|
|
7a8dc07a8a | ||
|
|
1fdac0bc09 | ||
|
|
804b942a36 | ||
|
|
6335d4378b | ||
|
|
cfc2189616 | ||
|
|
e06e032701 | ||
|
|
c9499c2c79 | ||
|
|
2bf43541bb | ||
|
|
0386c7266d | ||
|
|
7aa6ed9a5d | ||
|
|
b71325afa5 | ||
|
|
2cc29bdf77 | ||
|
|
b5d602abc4 | ||
|
|
33c45637ac | ||
|
|
662d4478d0 | ||
|
|
31d3809572 | ||
|
|
024ff4a309 | ||
|
|
9ae8d30b6b | ||
|
|
44349c10b0 | ||
|
|
506520a3c4 | ||
|
|
50f7020977 | ||
|
|
02a27a03cc | ||
|
|
4ad6bacf7b | ||
|
|
26ecc0fa44 | ||
|
|
2619befca6 |
@@ -1,31 +1,42 @@
|
||||
> 适配了最新版comfyui的py3.11 ,torch 2.1.2+cu121
|
||||
|
||||
### Workflow-to-APP 🚀🚗🚚🏃
|
||||
## 🚀🚗🚚🏃 Workflow-to-APP
|
||||
- 新增AppInfo节点,可以通过简单的配置,把workflow转变为一个Web APP。
|
||||
- 支持多个web app 切换
|
||||
- 发布为app的workflow,可以在右键里再次编辑了
|
||||
- web app可以设置分类,在comfyui右键菜单可以编辑更新web app
|
||||
|
||||
|
||||
- Support multiple web app switching.
|
||||
- Add the AppInfo node, which allows you to transform the workflow into a web app by simple configuration.
|
||||
- The workflow, which is now released as an app, can also be edited again by right-clicking.
|
||||
- The web app can be configured with categories, and the web app can be edited and updated in the right-click menu of ComfyUI.
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Example:
|
||||
- workflow
|
||||

|
||||
[text-to-image](./workflow/Text-to-Image-app.json)
|
||||
|
||||
APP-JSON:
|
||||
- [text-to-image](./example/text-to-image_1_Wed%20Dec%2027%202023.json)
|
||||
- [image-to-image](./example/image-to-image_1_Wed%20Dec%2027%202023.json)
|
||||
- [text-to-image](./example/Text-to-Image_3.json)
|
||||
- [image-to-image](./example/Image-to-Image_2.json)
|
||||
- text-to-text
|
||||
|
||||
> 暂时支持6种节点作为界面上的输入节点:Load Image、CLIPTextEncode、TextInput_、FloatSlider、IntNumber、CheckpointLoaderSimple、LoraLoader
|
||||
> 暂时支持8种节点作为界面上的输入节点:Load Image、CLIPTextEncode、PromptSlide、TextInput_、Color、FloatSlider、IntNumber、CheckpointLoaderSimple、LoraLoader
|
||||
|
||||
> 输出节点:PreviewImage 、SaveImage、ShowTextForGPT、VHS_VideoCombine
|
||||
> 输出节点:PreviewImage 、SaveImage、ShowTextForGPT、VHS_VideoCombine、PromptImage
|
||||
|
||||
> seed统一输入控件,支持:SamplerCustom、KSampler
|
||||
|
||||
|
||||
### Real-time Design
|
||||
## 🏃🚗🚚🚀 Real-time Design
|
||||
> ScreenShareNode & FloatingVideoNode. Now comfyui supports capturing screen pixel streams from any software and can be used for LCM-Lora integration. Let's get started with implementation and design! 💻🌐
|
||||
|
||||

|
||||
@@ -51,9 +62,26 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
|
||||
[workflow-5](./workflow/5-gpt-workflow.json)
|
||||
|
||||
### 3D
|
||||

|
||||
[workflow](./workflow/3D-workflow.json)
|
||||
|
||||
## Prompt
|
||||
> PromptSlide
|
||||

|
||||
|
||||
<!--  -->
|
||||
|
||||
> randomPrompt
|
||||
|
||||

|
||||
|
||||
> ClipInterrogator
|
||||
|
||||
[add clip-interrogator](https://github.com/pharmapsychotic/clip-interrogator)
|
||||
|
||||
> PromptImage & PromptSimplification,Assist in simplifying prompt words, comparing images and prompt word nodes.
|
||||
|
||||
> ChinesePrompt && PromptGenerate,中文prompt节点,直接用中文书写你的prompt
|
||||
|
||||

|
||||
|
||||
|
||||
### Layers
|
||||
@@ -64,6 +92,11 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||

|
||||
|
||||
|
||||
### 3D
|
||||

|
||||
[workflow](./workflow/3D-workflow.json)
|
||||
|
||||
|
||||
### LoadImagesFromLocal
|
||||
> Monitor changes to images in a local folder, and trigger real-time execution of workflows, supporting common image formats, especially PSD format, in conjunction with Photoshop.
|
||||
|
||||
@@ -82,6 +115,7 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
|
||||
- [Added DynamicDelayByText, enabling delayed execution based on input text length.](./workflow/audio-chatgpt-workflow.json)
|
||||
|
||||
|
||||
## Other Nodes
|
||||
|
||||

|
||||
@@ -89,9 +123,7 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
|
||||
[workflow-1](./workflow/1-workflow.json)
|
||||
|
||||
> randomPrompt
|
||||
|
||||

|
||||
|
||||
> TransparentImage
|
||||
|
||||
@@ -131,25 +163,16 @@ An improvement has been made to directly redirect to GitHub to search for missin
|
||||

|
||||
|
||||
|
||||
### Update
|
||||
v0.8.0 🚀🚗🚚🏃 LaMaInpainting
|
||||
- 新增 LaMaInpainting
|
||||
- 优化color节点的输出
|
||||
- 修复高清显示屏上定位节点不准的情况
|
||||
|
||||
- Add LaMaInpainting
|
||||
- Optimize the output of the color node
|
||||
- Fix the issue of inaccurate positioning node on high-definition display screens
|
||||
|
||||
|
||||
### Models
|
||||
[Download CLIPSeg](https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main), move to : models/clipseg
|
||||
|
||||
[Download lama](https://github.com/enesmsahin/simple-lama-inpainting/releases/download/v0.1.0/big-lama.pt), move to : models/lama
|
||||
|
||||
<!-- ### Workflow
|
||||
[Workflow](./workflow.md) -->
|
||||
[Download Salesforce/blip-image-captioning-base](https://huggingface.co/Salesforce/blip-image-captioning-base), move to : models/clip_interrogator/Salesforce/blip-image-captioning-base
|
||||
|
||||
[Download succinctly/text2image-prompt-generator](https://huggingface.co/succinctly/text2image-prompt-generator/tree/main),move to:text_generator/text2image-prompt-generator
|
||||
|
||||
[Download Helsinki-NLP/opus-mt-zh-en](https://huggingface.co/Helsinki-NLP/opus-mt-zh-en/tree/main),move to:prompt_generator/opus-mt-zh-en
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -190,9 +213,6 @@ pip3 install -r requirements.txt
|
||||
#### discussions:
|
||||
[discussions](https://github.com/shadowcz007/comfyui-mixlab-nodes/discussions)
|
||||
|
||||
### TODO:
|
||||
- 音频播放节点:带可视化、支持多音轨、可配置音轨音量
|
||||
- vector https://github.com/GeorgLegato/stable-diffusion-webui-vectorstudio
|
||||
|
||||
|
||||
<picture>
|
||||
|
||||
+166
-30
@@ -133,8 +133,38 @@ def create_for_https():
|
||||
return (crt,key)
|
||||
|
||||
|
||||
|
||||
# workflow 目录下的所有json
|
||||
def read_workflow_json_files_all(folder_path):
|
||||
print('#read_workflow_json_files_all',folder_path)
|
||||
json_files = []
|
||||
for root, dirs, files in os.walk(folder_path):
|
||||
for file in files:
|
||||
if file.endswith('.json'):
|
||||
json_files.append(os.path.join(root, file))
|
||||
|
||||
data = []
|
||||
for file_path in json_files:
|
||||
try:
|
||||
with open(file_path) as json_file:
|
||||
json_data = json.load(json_file)
|
||||
creation_time = datetime.datetime.fromtimestamp(os.path.getctime(file_path))
|
||||
numeric_timestamp = creation_time.timestamp()
|
||||
file_info = {
|
||||
'filename': os.path.basename(file_path),
|
||||
'category': os.path.dirname(file_path),
|
||||
'data': json_data,
|
||||
'date': numeric_timestamp
|
||||
}
|
||||
data.append(file_info)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
sorted_data = sorted(data, key=lambda x: x['date'], reverse=True)
|
||||
return sorted_data
|
||||
|
||||
# workflow
|
||||
def read_workflow_json_files(folder_path):
|
||||
def read_workflow_json_files(folder_path ):
|
||||
json_files = []
|
||||
for filename in os.listdir(folder_path):
|
||||
if filename.endswith('.json'):
|
||||
@@ -169,29 +199,46 @@ def get_workflows():
|
||||
workflows=read_workflow_json_files(workflow_path)
|
||||
return workflows
|
||||
|
||||
def get_my_workflow_for_app(filename="my_workflow_app.json"):
|
||||
def get_my_workflow_for_app(filename="my_workflow_app.json",category="",is_all=False):
|
||||
app_path=os.path.join(current_path, "app")
|
||||
if not os.path.exists(app_path):
|
||||
os.mkdir(app_path)
|
||||
|
||||
category_path=os.path.join(app_path,category)
|
||||
if not os.path.exists(category_path):
|
||||
os.mkdir(category_path)
|
||||
|
||||
apps=[]
|
||||
if filename==None:
|
||||
data=read_workflow_json_files(app_path)
|
||||
|
||||
#TODO 支持目录内遍历
|
||||
if is_all:
|
||||
data=read_workflow_json_files_all(category_path)
|
||||
else:
|
||||
data=read_workflow_json_files(category_path)
|
||||
|
||||
i=0
|
||||
for item in data:
|
||||
# print(item)
|
||||
try:
|
||||
x=item["data"]
|
||||
if i==0:
|
||||
apps.append({
|
||||
"filename":item["filename"],
|
||||
# "category":item['category'],
|
||||
"data":x,
|
||||
"date":item["date"]
|
||||
"date":item["date"],
|
||||
})
|
||||
else:
|
||||
category=''
|
||||
if 'category' in x['app']:
|
||||
category=x['app']['category']
|
||||
apps.append({
|
||||
"filename":item["filename"],
|
||||
"category":category,
|
||||
"data":{
|
||||
"app":{
|
||||
"category":category,
|
||||
"description":x['app']['description'],
|
||||
"filename":(x['app']['filename'] if 'filename' in x['app'] else "") ,
|
||||
"icon":(x['app']['icon'] if 'icon' in x['app'] else None),
|
||||
@@ -205,7 +252,7 @@ def get_my_workflow_for_app(filename="my_workflow_app.json"):
|
||||
except Exception as e:
|
||||
print("发生异常:", str(e))
|
||||
else:
|
||||
app_workflow_path=os.path.join(app_path, filename)
|
||||
app_workflow_path=os.path.join(category_path, filename)
|
||||
# print('app_workflow_path: ',app_workflow_path)
|
||||
try:
|
||||
with open(app_workflow_path) as json_file:
|
||||
@@ -216,17 +263,22 @@ def get_my_workflow_for_app(filename="my_workflow_app.json"):
|
||||
except Exception as e:
|
||||
print("发生异常:", str(e))
|
||||
|
||||
if len(apps)==1:
|
||||
data=read_workflow_json_files(app_path)
|
||||
if len(apps)==1 and category!='' and category!=None:
|
||||
data=read_workflow_json_files(category_path)
|
||||
|
||||
for item in data:
|
||||
x=item["data"]
|
||||
print(apps[0]['filename'] ,item["filename"])
|
||||
# print(apps[0]['filename'] ,item["filename"])
|
||||
if apps[0]['filename']!=item["filename"]:
|
||||
category=''
|
||||
if 'category' in x['app']:
|
||||
category=x['app']['category']
|
||||
apps.append({
|
||||
"filename":item["filename"],
|
||||
# "category":category,
|
||||
"data":{
|
||||
"app":{
|
||||
"category":category,
|
||||
"description":x['app']['description'],
|
||||
"filename":(x['app']['filename'] if 'filename' in x['app'] else "") ,
|
||||
"icon":(x['app']['icon'] if 'icon' in x['app'] else None),
|
||||
@@ -245,11 +297,16 @@ def save_workflow_json(data):
|
||||
json.dump(data, file)
|
||||
return workflow_path
|
||||
|
||||
def save_workflow_for_app(data,filename="my_workflow_app.json"):
|
||||
def save_workflow_for_app(data,filename="my_workflow_app.json",category=""):
|
||||
app_path=os.path.join(current_path, "app")
|
||||
if not os.path.exists(app_path):
|
||||
os.mkdir(app_path)
|
||||
app_workflow_path=os.path.join(app_path, filename)
|
||||
|
||||
category_path=os.path.join(app_path,category)
|
||||
if not os.path.exists(category_path):
|
||||
os.mkdir(category_path)
|
||||
|
||||
app_workflow_path=os.path.join(category_path, filename)
|
||||
|
||||
try:
|
||||
output_str = json.dumps(data['output'])
|
||||
@@ -294,33 +351,59 @@ async def new_request(self, method, url, *args, **kwargs):
|
||||
|
||||
# 应用 Monkey Patch
|
||||
aiohttp.ClientSession._request = new_request
|
||||
import socket
|
||||
|
||||
async def check_port_available(address, port):
|
||||
#检查端口是否可用
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
try:
|
||||
sock.bind((address, port))
|
||||
return True
|
||||
except socket.error:
|
||||
return False
|
||||
|
||||
# https
|
||||
async def new_start(self, address, port, verbose=True, call_on_start=None):
|
||||
|
||||
|
||||
try:
|
||||
runner = web.AppRunner(self.app, access_log=None)
|
||||
await runner.setup()
|
||||
|
||||
if not await check_port_available(address, port):
|
||||
raise RuntimeError(f"Port {port} is already in use.")
|
||||
|
||||
site = web.TCPSite(runner, address, port)
|
||||
await site.start()
|
||||
|
||||
import ssl
|
||||
crt,key=create_for_https()
|
||||
crt, key = create_for_https()
|
||||
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ssl_context.load_cert_chain(crt,key)
|
||||
site2 = web.TCPSite(runner, address, port+1,ssl_context=ssl_context)
|
||||
await site2.start()
|
||||
ssl_context.load_cert_chain(crt, key)
|
||||
|
||||
success = False
|
||||
for i in range(10): # 尝试最多10次
|
||||
if await check_port_available(address, port + 1 + i):
|
||||
https_port = port + 1 + i
|
||||
site2 = web.TCPSite(runner, address, https_port, ssl_context=ssl_context)
|
||||
await site2.start()
|
||||
success = True
|
||||
break
|
||||
|
||||
if not success:
|
||||
raise RuntimeError(f"Ports {port + 1} to {port + 10} are all in use.")
|
||||
|
||||
if address == '':
|
||||
address = '0.0.0.0'
|
||||
if verbose:
|
||||
# print('\033[91mMixlab Nodes: \033[93mLoaded\033[0m')
|
||||
print("\033[93mStarting server\n")
|
||||
print("\033[93mTo see the GUI go to: http://{}:{}".format(address, port))
|
||||
print("\033[93mTo see the GUI go to: https://{}:{}\033[0m".format(address, port+1))
|
||||
print("\033[93mTo see the GUI go to: https://{}:{}\033[0m".format(address, https_port))
|
||||
if call_on_start is not None:
|
||||
call_on_start(address, port)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error starting the server: {e}")
|
||||
|
||||
# import webbrowser
|
||||
# if os.name == 'nt' and address == '0.0.0.0':
|
||||
# address = '127.0.0.1'
|
||||
@@ -371,17 +454,26 @@ async def mixlab_workflow_hander(request):
|
||||
'file_path':file_path
|
||||
}
|
||||
elif data['task']=='save_app':
|
||||
file_path=save_workflow_for_app(data['data'],data['filename'])
|
||||
category=""
|
||||
if "category" in data:
|
||||
category=data['category']
|
||||
file_path=save_workflow_for_app(data['data'],data['filename'],category)
|
||||
result={
|
||||
'status':'success',
|
||||
'file_path':file_path
|
||||
}
|
||||
elif data['task']=='my_app':
|
||||
filename=None
|
||||
category=""
|
||||
admin=False
|
||||
if 'filename' in data:
|
||||
filename=data['filename']
|
||||
if 'category' in data:
|
||||
category=data['category']
|
||||
if 'admin' in data:
|
||||
admin=data['admin']
|
||||
result={
|
||||
'data':get_my_workflow_for_app(filename),
|
||||
'data':get_my_workflow_for_app(filename,category,admin),
|
||||
'status':'success',
|
||||
}
|
||||
elif data['task']=='list':
|
||||
@@ -411,6 +503,11 @@ async def nodes_map_hander(request):
|
||||
# 把插件自定义的路由添加到comfyui server里
|
||||
def new_add_routes(self):
|
||||
import nodes
|
||||
try:
|
||||
self.user_manager.add_routes(self.routes)
|
||||
except:
|
||||
print('pls update')
|
||||
|
||||
self.app.add_routes(routes)
|
||||
self.app.add_routes(self.routes)
|
||||
for name, dir in nodes.EXTENSION_WEB_DIRS.items():
|
||||
@@ -437,22 +534,29 @@ PromptServer.add_routes=new_add_routes
|
||||
|
||||
|
||||
# 导入节点
|
||||
from .nodes.PromptNode import RandomPrompt
|
||||
from .nodes.ImageNode import NoiseImage,TransparentImage,LoadImagesFromPath,LoadImagesFromURL,ResizeImage,TextImage,SvgImage,Image3D,ShowLayer,NewLayer,MergeLayers,AreaToMask,SmoothMask,FeatheredMask,SplitLongMask,ImageCropByAlpha,EnhanceImage,FaceToMask
|
||||
from .nodes.PromptNode import RandomPrompt,PromptSlide,PromptSimplification,PromptImage
|
||||
from .nodes.ImageNode import GetImageSize_,MirroredImage,ImageColorTransfer,NoiseImage,TransparentImage,GradientImage,LoadImagesFromPath,LoadImagesFromURL,ResizeImage,TextImage,SvgImage,Image3D,ShowLayer,NewLayer,MergeLayers,AreaToMask,SmoothMask,FeatheredMask,SplitLongMask,ImageCropByAlpha,EnhanceImage,FaceToMask
|
||||
from .nodes.Vae import VAELoader,VAEDecode
|
||||
from .nodes.ScreenShareNode import ScreenShareNode,FloatingVideo
|
||||
from .nodes.Clipseg import CLIPSeg,CombineMasks
|
||||
from .nodes.ChatGPT import ChatGPTNode,ShowTextForGPT,CharacterInText
|
||||
from .nodes.Audio import GamePal,SpeechRecognition,SpeechSynthesis
|
||||
from .nodes.Utils import AppInfo,IntNumber,FloatSlider,TextInput,ColorInput,FontInput,TextToNumber,DynamicDelayProcessor,LimitNumber,SwitchByIndex,GetImageSize_,MultiplicationNode
|
||||
from .nodes.Lama import LaMaInpainting
|
||||
from .nodes.Utils import TESTNODE_,AppInfo,IntNumber,FloatSlider,TextInput,ColorInput,FontInput,TextToNumber,DynamicDelayProcessor,LimitNumber,SwitchByIndex,MultiplicationNode
|
||||
|
||||
|
||||
|
||||
# 要导出的所有节点及其名称的字典
|
||||
# 注意:名称应全局唯一
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"AppInfo":AppInfo,
|
||||
"TESTNODE_":TESTNODE_,
|
||||
"RandomPrompt":RandomPrompt,
|
||||
"PromptSlide":PromptSlide,
|
||||
"PromptSimplification":PromptSimplification,
|
||||
"PromptImage":PromptImage,
|
||||
"MirroredImage":MirroredImage,
|
||||
"NoiseImage":NoiseImage,
|
||||
"GradientImage":GradientImage,
|
||||
"TransparentImage":TransparentImage,
|
||||
"ResizeImageMixlab":ResizeImage,
|
||||
"LoadImagesFromPath":LoadImagesFromPath,
|
||||
@@ -461,6 +565,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"EnhanceImage":EnhanceImage,
|
||||
"SvgImage":SvgImage,
|
||||
"3DImage":Image3D,
|
||||
"ImageColorTransfer":ImageColorTransfer,
|
||||
"ShowLayer":ShowLayer,
|
||||
"NewLayer":NewLayer,
|
||||
"MergeLayers":MergeLayers,
|
||||
@@ -492,7 +597,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"GetImageSize_":GetImageSize_,
|
||||
"SwitchByIndex":SwitchByIndex,
|
||||
"LimitNumber":LimitNumber,
|
||||
"LaMaInpainting":LaMaInpainting
|
||||
# "LaMaInpainting":LaMaInpainting
|
||||
# "GamePal":GamePal
|
||||
}
|
||||
|
||||
@@ -513,14 +618,45 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SpeechRecognition":"SpeechRecognition ♾️Mixlab",
|
||||
"3DImage":"3DImage ♾️Mixlab",
|
||||
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab",
|
||||
"LaMaInpainting":"LaMaInpainting ♾️Mixlab"
|
||||
|
||||
# "GamePal":"GamePal ♾️Mixlab"
|
||||
"LaMaInpainting":"LaMaInpainting ♾️Mixlab",
|
||||
"PromptSlide":"PromptSlide ♾️Mixlab",
|
||||
"PromptGenerate_Mix":"PromptGenerate ♾️Mixlab",
|
||||
"ChinesePrompt_Mix":"ChinesePrompt ♾️Mixlab",
|
||||
"GamePal":"GamePal ♾️Mixlab"
|
||||
}
|
||||
|
||||
# web ui的节点功能
|
||||
WEB_DIRECTORY = "./web"
|
||||
|
||||
print('--------------')
|
||||
print('\033[91m ### Mixlab Nodes: \033[93mLoaded\033[0m')
|
||||
print('--------------')
|
||||
print('\033[91m ### Mixlab Nodes: \033[93mLoaded')
|
||||
|
||||
try:
|
||||
from .nodes.Lama import LaMaInpainting
|
||||
print('LaMaInpainting.available',LaMaInpainting.available)
|
||||
if LaMaInpainting.available:
|
||||
NODE_CLASS_MAPPINGS['LaMaInpainting']=LaMaInpainting
|
||||
except:
|
||||
print('LaMaInpainting.available',False)
|
||||
|
||||
try:
|
||||
from .nodes.ClipInterrogator import ClipInterrogator
|
||||
print('ClipInterrogator.available',ClipInterrogator.available)
|
||||
if ClipInterrogator.available:
|
||||
NODE_CLASS_MAPPINGS['ClipInterrogator']=ClipInterrogator
|
||||
except:
|
||||
print('ClipInterrogator.available',False)
|
||||
|
||||
try:
|
||||
from .nodes.TextGenerateNode import PromptGenerate,ChinesePrompt
|
||||
print('PromptGenerate.available',PromptGenerate.available)
|
||||
if PromptGenerate.available:
|
||||
NODE_CLASS_MAPPINGS['PromptGenerate_Mix']=PromptGenerate
|
||||
print('ChinesePrompt.available',ChinesePrompt.available)
|
||||
if ChinesePrompt.available:
|
||||
NODE_CLASS_MAPPINGS['ChinesePrompt_Mix']=ChinesePrompt
|
||||
except:
|
||||
print('TextGenerateNode.available',False)
|
||||
|
||||
|
||||
print('\033[93m -------------- \033[0m')
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 2.4 MiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 477 KiB |
@@ -0,0 +1,30 @@
|
||||
Jony Ive
|
||||
Dieter Rams
|
||||
Philippe Starck
|
||||
Karim Rashid
|
||||
Yves Béhar
|
||||
Marc Newson
|
||||
Naoto Fukasawa
|
||||
Jonathan Adler
|
||||
Patricia Urquiola
|
||||
Ross Lovegrove
|
||||
Tom Dixon
|
||||
Jasper Morrison
|
||||
Charles Eames
|
||||
Ray Eames
|
||||
Achille Castiglioni
|
||||
Ron Arad
|
||||
Konstantin Grcic
|
||||
Marcel Wanders
|
||||
Maarten Baas
|
||||
Stefan Sagmeister
|
||||
Ingo Maurer
|
||||
Hella Jongerius
|
||||
Sam Hecht
|
||||
Kim Colin
|
||||
Jaime Hayon
|
||||
Michael Anastassiades
|
||||
Nendo
|
||||
Oki Sato
|
||||
Matali Crasset
|
||||
Tokujin Yoshioka
|
||||
@@ -0,0 +1,10 @@
|
||||
Chibi Anime Style
|
||||
Gakuen Anime Style
|
||||
Gekiga Anime Style
|
||||
Jidaimono Anime Style
|
||||
Kawaii Anime Style
|
||||
Mecha Anime Style
|
||||
Realistic Anime Style
|
||||
Semi-Realistic Anime Style
|
||||
Shoji Anime Style
|
||||
Kemonomimi Anime Style
|
||||
+2052
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
GoPro
|
||||
Drone
|
||||
polaroid
|
||||
black and white film
|
||||
Kodachrome
|
||||
shot on 8mm
|
||||
shot on 16mm
|
||||
shot on 35mm
|
||||
Microscopic
|
||||
Fisheye Lens
|
||||
Wide Angle
|
||||
Ultra-Wide Angle
|
||||
Panorama
|
||||
Short Exposure
|
||||
Long Exposure
|
||||
Double Exposure
|
||||
f2.8
|
||||
Depth of Field
|
||||
Soft Focus
|
||||
Deep Focus
|
||||
Shallow Focus
|
||||
Vanishing Point
|
||||
Vantage Point
|
||||
@@ -0,0 +1,30 @@
|
||||
Elegant evening gown
|
||||
Casual jeans and t-shirt
|
||||
Formal black suit
|
||||
Stylish leather jacket
|
||||
Flowy bohemian dress
|
||||
Sporty tracksuit
|
||||
Chic little black dress
|
||||
Trendy ripped jeans
|
||||
Classic white button-down shirt
|
||||
Cozy oversized sweater
|
||||
Sophisticated tailored blazer
|
||||
Quirky patterned leggings
|
||||
Striped sailor top
|
||||
Polished knee-length skirt
|
||||
Vintage-inspired floral dress
|
||||
Edgy motorcycle jacket
|
||||
Preppy polo shirt
|
||||
Boho maxi skirt
|
||||
Professional pinstripe suit
|
||||
Relaxed denim shorts
|
||||
Glamorous sequined dress
|
||||
Athletic running shoes
|
||||
Formal bow tie
|
||||
Casual baseball cap
|
||||
Stylish fedora hat
|
||||
Warm woolen scarf
|
||||
Comfortable cotton socks
|
||||
Trendy ankle boots
|
||||
Cute summer sandals
|
||||
Cozy pajama set
|
||||
@@ -0,0 +1,30 @@
|
||||
Happy
|
||||
Sad
|
||||
Angry
|
||||
Surprised
|
||||
Excited
|
||||
Worried
|
||||
Confused
|
||||
Disgusted
|
||||
Amused
|
||||
Bored
|
||||
Curious
|
||||
Embarrassed
|
||||
Frustrated
|
||||
Nervous
|
||||
Pleased
|
||||
Relieved
|
||||
Shy
|
||||
Tired
|
||||
Serious
|
||||
Silly
|
||||
Proud
|
||||
Grumpy
|
||||
Smug
|
||||
Sarcastic
|
||||
Flirty
|
||||
Skeptical
|
||||
Shocked
|
||||
Blissful
|
||||
Envious
|
||||
Mischievous
|
||||
@@ -4761,12 +4761,15 @@
|
||||
],
|
||||
"https://github.com/shadowcz007/comfyui-mixlab-nodes": [
|
||||
[
|
||||
"PromptGenerate_Mix",
|
||||
"ChinesePrompt_Mix",
|
||||
"3DImage",
|
||||
"AppInfo",
|
||||
"IntNumber",
|
||||
"FloatSlider",
|
||||
"ResizeImage",
|
||||
"NoiseImage",
|
||||
"PromptImage",
|
||||
"AreaToMask",
|
||||
"CLIPSeg_",
|
||||
"CharacterInText",
|
||||
@@ -4774,6 +4777,7 @@
|
||||
"Color",
|
||||
"CombineMasks_",
|
||||
"EnhanceImage",
|
||||
"GradientImage",
|
||||
"FaceToMask",
|
||||
"FeatheredMask",
|
||||
"FloatingVideo",
|
||||
@@ -4784,6 +4788,9 @@
|
||||
"MergeLayers",
|
||||
"NewLayer",
|
||||
"RandomPrompt",
|
||||
"PromptSlide",
|
||||
"PromptSimplification",
|
||||
"ClipInterrogator",
|
||||
"ScreenShare",
|
||||
"ShowLayer",
|
||||
"ShowTextForGPT",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
Mood Lighting
|
||||
Moody Lighting
|
||||
Studio Lighting
|
||||
Cove Lighting
|
||||
Soft Lighting
|
||||
Hard Lighting
|
||||
Volumetric Lighting
|
||||
Low-Key Lighting
|
||||
High-Key Lighting
|
||||
Epic Light
|
||||
Rembrandt Lighting
|
||||
Contre-Jour
|
||||
Veiling Flare
|
||||
Crepuscular Rays
|
||||
Rays of Shimmering Light
|
||||
Godrays
|
||||
@@ -0,0 +1,132 @@
|
||||
Aaron Siskind
|
||||
Alessio Albi
|
||||
Alfred Eisenstaedt
|
||||
Alfred Stieglitz
|
||||
Alyssa Monks
|
||||
André Kertész
|
||||
Andreas Gursky
|
||||
Andrew Wyeth
|
||||
Anne Geddes
|
||||
Annie Leibovitz
|
||||
Ansel Adams
|
||||
Arnold Newman
|
||||
August Sander
|
||||
Balthus
|
||||
Berenice Abbott
|
||||
Bill Brandt
|
||||
Bill Henson
|
||||
Brassaï (Gyula Halász)
|
||||
Brooke Shaden
|
||||
Bruce Davidson
|
||||
Bruce Weber
|
||||
Bunny Yeager
|
||||
Carleton Watkins
|
||||
Carrie Mae Weems
|
||||
Chuck Close
|
||||
Cindy Sherman
|
||||
Clarence H. White
|
||||
Claude Cahun
|
||||
Danny Lyon
|
||||
David LaChapelle
|
||||
Dawoud Bey
|
||||
Diane Arbus
|
||||
Don McCullin
|
||||
Dora Maar
|
||||
Dorothea Lange
|
||||
Duane Michals
|
||||
Eadweard Muybridge
|
||||
Edward Burtynsky
|
||||
Edward Curtis
|
||||
Edward Ruscha
|
||||
Edward Steichen
|
||||
Edward Weston
|
||||
Elliott Erwitt
|
||||
Ernst Haas
|
||||
Eugene Atget
|
||||
Fan Ho
|
||||
Francesca Woodman
|
||||
Frans Lanting
|
||||
Garry Winogrand
|
||||
Georges Melies
|
||||
Gerda Taro
|
||||
Gertrude Käsebier
|
||||
Gordon Parks
|
||||
Graciela Iturbide
|
||||
Gregory Crewdson
|
||||
Harold Edgerton
|
||||
Helen Levitt
|
||||
Helmut Newton
|
||||
Hendrik Kerstens
|
||||
Henri Cartier-Bresson
|
||||
Hugh Kretschmer
|
||||
Irving Penn
|
||||
Jacques Henri Lartigue
|
||||
James Nachtwey
|
||||
James Van Der Zee
|
||||
Jay Maisel
|
||||
Jerry Uelsmann
|
||||
Joel Peter Witkin
|
||||
Joel Sartore
|
||||
John Frederick William Herschel
|
||||
Josef Sudek
|
||||
Julia Margaret Cameron
|
||||
Karl Blossfeldt
|
||||
Larry Burrows
|
||||
László Moholy-Nagy (photography)
|
||||
Lee Jeffries
|
||||
Lewis Hine
|
||||
Lorna Simpson
|
||||
Lynsey Addario
|
||||
Margaret Bourke-White
|
||||
Mario Testino
|
||||
Martin Parr
|
||||
Martin Schoeller
|
||||
Mary Ellen Mark
|
||||
Mathew B. Brady
|
||||
Méret Oppenheim
|
||||
Meryl McMaster
|
||||
Mick Rock
|
||||
Miles Aldridge
|
||||
Minor Martin White
|
||||
Nan Goldin
|
||||
Nathan Wirth
|
||||
Olive Cotton
|
||||
Olivier Rousteing
|
||||
Patrick Demarchelier
|
||||
Paul Nicklen
|
||||
Paul Outerbridge
|
||||
Paul Strand
|
||||
Pete Souza
|
||||
Peter Dombrovskis
|
||||
Peter Henry Emerson
|
||||
Peter Lik
|
||||
Peter Lindbergh
|
||||
Philip-Lorca diCorcia
|
||||
Philippe Halsman
|
||||
Ralph Gibson
|
||||
Richard Avedon
|
||||
Robert Adams
|
||||
Robert Bechtle
|
||||
Robert Capa
|
||||
Robert Frank
|
||||
Robert Mapplethorpe
|
||||
Roger Fenton
|
||||
Ruth Bernhard
|
||||
Sally Mann
|
||||
Sebastião Salgado
|
||||
Shirin Neshat
|
||||
Stefan Gesell
|
||||
Steven Meisel
|
||||
Susan Meiselas
|
||||
Vivian Maier
|
||||
Vivian Maier
|
||||
Viviane Sassen
|
||||
Walker Evans
|
||||
Wes Anderson
|
||||
William Eggleston
|
||||
William Eugene Smith
|
||||
William Henry Fox Talbot
|
||||
Yinka Shonibare
|
||||
Yousuf Karsh
|
||||
Man Ray
|
||||
Robert Mapplethorpe
|
||||
@@ -0,0 +1,101 @@
|
||||
Doctor
|
||||
Teacher
|
||||
Engineer
|
||||
Lawyer
|
||||
Accountant
|
||||
Nurse
|
||||
Architect
|
||||
Chef
|
||||
Pilot
|
||||
Scientist
|
||||
Artist
|
||||
Writer
|
||||
Musician
|
||||
Actor
|
||||
Photographer
|
||||
Police officer
|
||||
Firefighter
|
||||
Dentist
|
||||
Pharmacist
|
||||
Veterinarian
|
||||
Electrician
|
||||
Plumber
|
||||
Carpenter
|
||||
Mechanic
|
||||
Farmer
|
||||
Astronaut
|
||||
Athlete
|
||||
Journalist
|
||||
Politician
|
||||
Economist
|
||||
Psychologist
|
||||
Social worker
|
||||
Librarian
|
||||
Translator
|
||||
Salesperson
|
||||
Entrepreneur
|
||||
Financial advisor
|
||||
Graphic designer
|
||||
Web developer
|
||||
Marketing manager
|
||||
Human resources manager
|
||||
Project manager
|
||||
Event planner
|
||||
Fashion designer
|
||||
Interior decorator
|
||||
Real estate agent
|
||||
Archaeologist
|
||||
Biologist
|
||||
Chemist
|
||||
Geologist
|
||||
Physicist
|
||||
Mathematician
|
||||
Historian
|
||||
Geographer
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
#MixCopilot
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
Vintage
|
||||
Grain
|
||||
Sepia
|
||||
High Key
|
||||
Low Key
|
||||
High Dynamic Range
|
||||
Cross Process
|
||||
Radial Blur
|
||||
Infrared
|
||||
Lomo
|
||||
Photocopy
|
||||
Pencil Sketch
|
||||
Pop Art
|
||||
Orton
|
||||
Mosaic
|
||||
Selective Black and White
|
||||
Torn Paper
|
||||
Tilt-Shift
|
||||
Double Exposure
|
||||
Polaroid
|
||||
Liquid Ink
|
||||
Color Splash
|
||||
Sketch
|
||||
Water Drops
|
||||
Polarizer
|
||||
Chinese Painting
|
||||
Water Droplets
|
||||
Polarization
|
||||
Color Inversion
|
||||
Fish-eye
|
||||
Soft Focus
|
||||
Solarization
|
||||
Posterize
|
||||
Comic Book
|
||||
Duotone
|
||||
Gradient Map
|
||||
Edge Detection
|
||||
Oil Painting
|
||||
Reflection
|
||||
Mirror
|
||||
ASCII Art
|
||||
Glitch
|
||||
Time-Lapse
|
||||
Day to Night
|
||||
Surreal
|
||||
Black and White
|
||||
Sepia Tone
|
||||
Vintage Film
|
||||
Grainy Texture
|
||||
High Key Lighting
|
||||
Low Key Lighting
|
||||
Cross Processed Film
|
||||
Infrared Photography
|
||||
Photocopy
|
||||
Pencil Drawing
|
||||
Pop Art Filter
|
||||
Mosaic Filter
|
||||
Selective Desaturation
|
||||
Torn Paper
|
||||
Tilt-Shift Photography
|
||||
Double Exposure
|
||||
Polaroid Style Frame
|
||||
Water Drops Texture
|
||||
Polarizer
|
||||
Chinese Painting
|
||||
Water Droplets Texture
|
||||
Polarization
|
||||
Color Inversion
|
||||
Fish-eye Lens
|
||||
Soft Focus
|
||||
Solarize Filter
|
||||
Edge Detection
|
||||
Oil Painting
|
||||
Reflection
|
||||
Mirror Image
|
||||
Time-Lapse Photography
|
||||
Day to Night Transition
|
||||
Surreal Art Style
|
||||
Abstract Expressionism
|
||||
Acrylic Painting
|
||||
Anime
|
||||
Art Deco
|
||||
Biomorphic Abstraction
|
||||
Black and White Photograph
|
||||
Cartoon
|
||||
Charcoal Sketch
|
||||
Chibi Anime
|
||||
Chinese Painting
|
||||
Classicist Painting
|
||||
Collage
|
||||
Concept Art
|
||||
Cyberpunk
|
||||
Dada Art
|
||||
Digital Art
|
||||
Fantasy Art
|
||||
Fashion Art
|
||||
Fashion Sketch
|
||||
Fish-Eye lens Photograph
|
||||
Goth Art
|
||||
Graffiti
|
||||
Harlem Renaissance
|
||||
High Key Photograph
|
||||
Hyperrealist Pencil Sketch
|
||||
Impressionist Painting
|
||||
Josei Anime
|
||||
Long Exposure Photograph
|
||||
Low Key Photograph
|
||||
Macro Photograph
|
||||
Manga
|
||||
Metal Sculpture
|
||||
Mid Century Modern Illustration
|
||||
Mixed Media
|
||||
Modern Art
|
||||
Moe Anime
|
||||
Nihonga
|
||||
Origami
|
||||
Paper Mache
|
||||
Pen and Ink
|
||||
Pencil Sketch
|
||||
Photograph
|
||||
Photorealism
|
||||
Pinup Art
|
||||
Romanticist Painting
|
||||
Sci-Fi Art
|
||||
Semi Realistic Fantasy Art
|
||||
Semi Realistic Cyberpunk Art
|
||||
Shallow Depth of Field Photograph
|
||||
Steam Punk Art
|
||||
Stone Sculpture
|
||||
Superhero Comic
|
||||
Surrealist Art
|
||||
Tempura Painting
|
||||
Underground Comic
|
||||
Watercolor Painting
|
||||
Zulu Urban Art
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3
-3
@@ -24,7 +24,7 @@ class SpeechRecognition:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/audio"
|
||||
CATEGORY = "♾️Mixlab/Audio"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
@@ -48,7 +48,7 @@ class SpeechSynthesis:
|
||||
OUTPUT_NODE = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
CATEGORY = "♾️Mixlab/audio"
|
||||
CATEGORY = "♾️Mixlab/Audio"
|
||||
|
||||
def run(self, text):
|
||||
# print(session_history)
|
||||
@@ -82,7 +82,7 @@ class GamePal:
|
||||
OUTPUT_NODE = True
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
CATEGORY = "♾️Mixlab/audio"
|
||||
CATEGORY = "♾️Mixlab/Audio"
|
||||
|
||||
def run(self, input_text,input_num,python_code):
|
||||
exec(python_code)
|
||||
|
||||
+2
-1
@@ -180,8 +180,9 @@ class ShowTextForGPT:
|
||||
CATEGORY = "♾️Mixlab/GPT"
|
||||
|
||||
def run(self, text):
|
||||
# print(session_history)
|
||||
# print(text)
|
||||
return {"ui": {"text": text}, "result": (text,)}
|
||||
|
||||
|
||||
|
||||
class CharacterInText:
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
import os,sys
|
||||
import folder_paths
|
||||
|
||||
from PIL import Image
|
||||
import importlib.util
|
||||
|
||||
import comfy.utils
|
||||
import numpy as np
|
||||
import json
|
||||
import torch
|
||||
import random
|
||||
|
||||
|
||||
# from clip_interrogator import Config, Interrogator
|
||||
|
||||
|
||||
global _available
|
||||
_available=False
|
||||
|
||||
def is_installed(package):
|
||||
try:
|
||||
spec = importlib.util.find_spec(package)
|
||||
except ModuleNotFoundError:
|
||||
return False
|
||||
return spec is not None
|
||||
|
||||
try:
|
||||
if is_installed('clip_interrogator')==False:
|
||||
import subprocess
|
||||
|
||||
# 安装
|
||||
print('#pip install clip-interrogator==0.6.0')
|
||||
|
||||
result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', 'clip-interrogator==0.6.0'], capture_output=True, text=True)
|
||||
|
||||
#检查命令执行结果
|
||||
if result.returncode == 0:
|
||||
print("#install success")
|
||||
from transformers import AutoProcessor, BlipForConditionalGeneration
|
||||
from clip_interrogator import Config, Interrogator
|
||||
_available=True
|
||||
else:
|
||||
print("#install error")
|
||||
|
||||
else:
|
||||
from transformers import AutoProcessor, BlipForConditionalGeneration
|
||||
from clip_interrogator import Config, Interrogator
|
||||
_available=True
|
||||
|
||||
except:
|
||||
_available=False
|
||||
|
||||
|
||||
def load_caption_model(model_path,config,t='blip-base'):
|
||||
dtype=torch.float16 if config.device == 'cuda' else torch.float32
|
||||
caption_model = BlipForConditionalGeneration.from_pretrained(model_path, torch_dtype=dtype)
|
||||
|
||||
caption_processor = AutoProcessor.from_pretrained(model_path)
|
||||
|
||||
caption_model.eval()
|
||||
if not config.caption_offload:
|
||||
caption_model = caption_model.to(config.device)
|
||||
|
||||
return (caption_model,caption_processor)
|
||||
|
||||
|
||||
|
||||
caption_model_path=os.path.join(folder_paths.models_dir, "clip_interrogator/Salesforce/blip-image-captioning-base")
|
||||
if not os.path.exists(caption_model_path):
|
||||
print(f"## clip_interrogator_model not found: {caption_model_path}, pls download from https://huggingface.co/Salesforce/blip-image-captioning-base")
|
||||
caption_model_path='Salesforce/blip-image-captioning-base'
|
||||
|
||||
cache_path=os.path.join(folder_paths.models_dir, "clip_interrogator")
|
||||
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
# Convert PIL to Tensor
|
||||
def pil2tensor(image):
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
|
||||
def image_analysis_fn(ci,image):
|
||||
image = image.convert('RGB')
|
||||
image_features = ci.image_to_features(image)
|
||||
|
||||
top_mediums = ci.mediums.rank(image_features, 5)
|
||||
top_artists = ci.artists.rank(image_features, 5)
|
||||
top_movements = ci.movements.rank(image_features, 5)
|
||||
top_trendings = ci.trendings.rank(image_features, 5)
|
||||
top_flavors = ci.flavors.rank(image_features, 5)
|
||||
|
||||
medium_ranks = {medium: sim for medium, sim in zip(top_mediums, ci.similarities(image_features, top_mediums))}
|
||||
artist_ranks = {artist: sim for artist, sim in zip(top_artists, ci.similarities(image_features, top_artists))}
|
||||
movement_ranks = {movement: sim for movement, sim in zip(top_movements, ci.similarities(image_features, top_movements))}
|
||||
trending_ranks = {trending: sim for trending, sim in zip(top_trendings, ci.similarities(image_features, top_trendings))}
|
||||
flavor_ranks = {flavor: sim for flavor, sim in zip(top_flavors, ci.similarities(image_features, top_flavors))}
|
||||
|
||||
return {
|
||||
"medium_ranks":medium_ranks,
|
||||
"artist_ranks":artist_ranks,
|
||||
"movement_ranks":movement_ranks,
|
||||
"trending_ranks":trending_ranks,
|
||||
"flavor_ranks":flavor_ranks
|
||||
}
|
||||
|
||||
|
||||
def generate_sentences(data):
|
||||
sentences = []
|
||||
|
||||
# Get the length of data
|
||||
data_length = len(data)
|
||||
|
||||
# Use a recursive function to handle variable-length data
|
||||
def generate_recursive(index, current_sentence, current_score):
|
||||
# Check if recursion is complete
|
||||
if index == data_length:
|
||||
sentences.append({"sentence": current_sentence, "score": current_score})
|
||||
return
|
||||
|
||||
# Get the current level data
|
||||
current_data = data[index]
|
||||
|
||||
# Iterate through the current level data
|
||||
for phrase in current_data:
|
||||
sentence = current_sentence + ("," if current_sentence.strip() else "") + phrase
|
||||
score = current_score + current_data[phrase]
|
||||
generate_recursive(index + 1, sentence, score)
|
||||
|
||||
# Start recursive generation of sentences
|
||||
generate_recursive(0, "", 0)
|
||||
|
||||
# Sort the generated sentences by score in descending order
|
||||
sentences.sort(key=lambda x: x["score"], reverse=True)
|
||||
|
||||
def get_random_elements(elements, num):
|
||||
return random.sample(elements, num)
|
||||
|
||||
ps = get_random_elements(sentences, 5)
|
||||
ps = [s["sentence"] for s in sorted(ps, key=lambda x: x["score"], reverse=True)]
|
||||
|
||||
return ps
|
||||
|
||||
|
||||
|
||||
|
||||
def image_to_prompt(ci,image, mode):
|
||||
ci.config.chunk_size = 2048 if ci.config.clip_model_name == "ViT-L-14/openai" else 1024
|
||||
ci.config.flavor_intermediate_count = 2048 if ci.config.clip_model_name == "ViT-L-14/openai" else 1024
|
||||
image = image.convert('RGB')
|
||||
if mode == 'best':
|
||||
return ci.interrogate(image)
|
||||
elif mode == 'classic':
|
||||
return ci.interrogate_classic(image)
|
||||
elif mode == 'fast':
|
||||
return ci.interrogate_fast(image)
|
||||
elif mode == 'negative':
|
||||
return ci.interrogate_negative(image)
|
||||
|
||||
# image = Image.open(image_path).convert('RGB')
|
||||
# ci = Interrogator(Config(clip_model_name="ViT-L-14/openai"))
|
||||
# print(ci.interrogate(image))
|
||||
|
||||
|
||||
class ClipInterrogator:
|
||||
|
||||
global _available
|
||||
available=_available
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"image": ("IMAGE",),
|
||||
"prompt_mode": (['fast','classic','best','negative'],),
|
||||
"image_analysis": (["off","on"],),
|
||||
},
|
||||
|
||||
# "optional":{
|
||||
# "output":("CLIPINTERROGATOR", {"multiline": True,"default": "", "dynamicPrompts": False})
|
||||
# },
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING","STRING",)
|
||||
RETURN_NAMES = ("prompt","random_samples",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,True,)
|
||||
global ci
|
||||
ci = None
|
||||
def run(self,image,prompt_mode,image_analysis):
|
||||
global ci
|
||||
|
||||
prompt_mode=prompt_mode[0]
|
||||
analysis=image_analysis[0]
|
||||
|
||||
prompt_result=[]
|
||||
analysis_result=[]
|
||||
|
||||
# 进度条
|
||||
pbar = comfy.utils.ProgressBar(len(image)*(2 if analysis=='on' else 1))
|
||||
|
||||
if ci==None:
|
||||
config=Config(
|
||||
clip_model_name="ViT-L-14/openai",
|
||||
device="cuda" if torch.cuda.is_available() else "cpu",
|
||||
download_cache=True,
|
||||
clip_model_path=cache_path,
|
||||
cache_path=cache_path
|
||||
)
|
||||
config.apply_low_vram_defaults()
|
||||
|
||||
caption_model,caption_processor=load_caption_model(caption_model_path,config)
|
||||
|
||||
config.caption_model= caption_model
|
||||
config.caption_processor= caption_processor
|
||||
|
||||
ci = Interrogator(config)
|
||||
# else:
|
||||
# simple_lama.model.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
for i in range(len(image)):
|
||||
im=image[i]
|
||||
|
||||
im=tensor2pil(im)
|
||||
im=im.convert('RGB')
|
||||
|
||||
if analysis=='on':
|
||||
analysis_res=image_analysis_fn(ci,im)
|
||||
analysis_result.append( analysis_res )
|
||||
pbar.update(1)
|
||||
|
||||
prompt=image_to_prompt(ci,im,prompt_mode)
|
||||
pbar.update(1)
|
||||
prompt_result.append(prompt)
|
||||
|
||||
|
||||
# result.save("inpainted.png")
|
||||
if ci.config.clip_offload and not ci.clip_offloaded:
|
||||
ci.clip_model = ci.clip_model.to('cpu')
|
||||
ci.clip_offloaded = True
|
||||
|
||||
if ci.config.caption_offload and not ci.caption_offloaded:
|
||||
ci.caption_model = ci.caption_model.to('cpu')
|
||||
ci.caption_offloaded = True
|
||||
|
||||
# analysis_result=[]
|
||||
# items = app.graph.getNodeById(31).widgets[2].value["items"]
|
||||
|
||||
random_samples=[]
|
||||
|
||||
for r in analysis_result:
|
||||
random_sample = generate_sentences([r['medium_ranks'], r['artist_ranks'],r['movement_ranks'],r['trending_ranks'],r['flavor_ranks']])
|
||||
for s in random_sample:
|
||||
random_samples.append(s)
|
||||
# print(len(random_samples))
|
||||
# print('-----')
|
||||
# print( random_samples)
|
||||
return {
|
||||
"ui":{
|
||||
"prompt": prompt_result,
|
||||
"analysis":analysis_result,
|
||||
"random_samples":random_samples
|
||||
},
|
||||
"result": (prompt_result,random_samples,)}
|
||||
+5
-5
@@ -31,11 +31,11 @@ logger = logging.getLogger('CLIPSeg nodes')
|
||||
clipseg_model_dir = os.path.join(folder_paths.models_dir, "clipseg")
|
||||
|
||||
if not os.path.exists(clipseg_model_dir):
|
||||
print(f"## clipseg model not found: {clipseg_model_dir},pls download from https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main")
|
||||
clipseg_model_dir='CIDAS/clipseg-rd64-refined'
|
||||
|
||||
"""Helper methods for CLIPSeg nodes"""
|
||||
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
@@ -106,13 +106,13 @@ class CLIPSeg:
|
||||
},
|
||||
"optional":
|
||||
{
|
||||
"blur": ("FLOAT", {"min": 0, "max": 15, "step": 0.1, "default": 7}),
|
||||
"threshold": ("FLOAT", {"min": 0, "max": 1, "step": 0.05, "default": 0.4}),
|
||||
"blur": ("FLOAT", {"min": 0, "max": 15, "step": 0.1, "default": 3}),
|
||||
"threshold": ("FLOAT", {"min": 0, "max": 1, "step": 0.05, "default": 0.3}),
|
||||
"dilation_factor": ("INT", {"min": 0, "max": 10, "step": 1, "default": 4}),
|
||||
}
|
||||
}
|
||||
|
||||
CATEGORY = "♾️Mixlab/mask"
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
RETURN_TYPES = ("MASK", "IMAGE", "IMAGE",)
|
||||
RETURN_NAMES = ("Mask","Heatmap Mask", "BW Mask")
|
||||
|
||||
@@ -218,7 +218,7 @@ class CombineMasks:
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = "♾️Mixlab/mask"
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
RETURN_TYPES = ("MASK", "IMAGE", "IMAGE",)
|
||||
RETURN_NAMES = ("Combined Mask","Heatmap Mask", "BW Mask")
|
||||
|
||||
|
||||
+554
-103
@@ -1,6 +1,7 @@
|
||||
import numpy as np
|
||||
import requests
|
||||
import torch
|
||||
# from PIL import Image, ImageDraw
|
||||
from PIL import Image, ImageOps,ImageFilter,ImageEnhance,ImageDraw,ImageSequence, ImageFont
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import base64,os,random
|
||||
@@ -9,7 +10,7 @@ import folder_paths
|
||||
import json,io
|
||||
from comfy.cli_args import args
|
||||
import cv2
|
||||
|
||||
import math
|
||||
from .Watcher import FolderWatcher
|
||||
|
||||
|
||||
@@ -26,6 +27,52 @@ def pil2tensor(image):
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
|
||||
# 颜色迁移
|
||||
# Color-Transfer-between-Images https://github.com/chia56028/Color-Transfer-between-Images/blob/master/color_transfer.py
|
||||
|
||||
def get_mean_and_std(x):
|
||||
x_mean, x_std = cv2.meanStdDev(x)
|
||||
x_mean = np.hstack(np.around(x_mean,2))
|
||||
x_std = np.hstack(np.around(x_std,2))
|
||||
return x_mean, x_std
|
||||
|
||||
def color_transfer(source,target):
|
||||
# sources = ['s1','s2','s3','s4','s5','s6']
|
||||
# targets = ['t1','t2','t3','t4','t5','t6']
|
||||
|
||||
# 将PIL的Image类型转换为OpenCV的numpy数组
|
||||
source = cv2.cvtColor(np.array(source), cv2.COLOR_RGB2LAB)
|
||||
target = cv2.cvtColor(np.array(target), cv2.COLOR_RGB2LAB)
|
||||
|
||||
s_mean, s_std = get_mean_and_std(source)
|
||||
t_mean, t_std = get_mean_and_std(target)
|
||||
|
||||
height, width, channel = source.shape
|
||||
|
||||
for i in range(0,height):
|
||||
for j in range(0,width):
|
||||
for k in range(0,channel):
|
||||
x = source[i,j,k]
|
||||
x = ((x-s_mean[k])*(t_std[k]/s_std[k]))+t_mean[k]
|
||||
# round or +0.5
|
||||
x = round(x)
|
||||
# boundary check
|
||||
x = 0 if x<0 else x
|
||||
x = 255 if x>255 else x
|
||||
source[i,j,k] = x
|
||||
|
||||
source = cv2.cvtColor(source,cv2.COLOR_LAB2RGB)
|
||||
|
||||
# 创建PIL图像对象
|
||||
image_pil = Image.fromarray(source)
|
||||
|
||||
return image_pil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def naive_cutout(img, mask,invert=True):
|
||||
"""
|
||||
Perform a simple cutout operation on an image using a mask.
|
||||
@@ -155,6 +202,59 @@ def get_not_transparent_area(image):
|
||||
return (x, y, w, h)
|
||||
|
||||
|
||||
|
||||
|
||||
def generate_gradient_image(width, height, start_color_hex, end_color_hex):
|
||||
image = Image.new('RGBA', (width, height))
|
||||
draw = ImageDraw.Draw(image)
|
||||
|
||||
if len(start_color_hex) == 7:
|
||||
start_color_hex += "FF"
|
||||
if len(end_color_hex) == 7:
|
||||
end_color_hex += "FF"
|
||||
|
||||
start_color_hex = start_color_hex.lstrip("#")
|
||||
end_color_hex = end_color_hex.lstrip("#")
|
||||
|
||||
# 将十六进制颜色代码转换为RGBA元组,包括透明度
|
||||
start_color = tuple(int(start_color_hex[i:i+2], 16) for i in (0, 2, 4, 6))
|
||||
end_color = tuple(int(end_color_hex[i:i+2], 16) for i in (0, 2, 4, 6))
|
||||
|
||||
for y in range(height):
|
||||
# 计算当前行的颜色
|
||||
r = int(start_color[0] + (end_color[0] - start_color[0]) * y / height)
|
||||
g = int(start_color[1] + (end_color[1] - start_color[1]) * y / height)
|
||||
b = int(start_color[2] + (end_color[2] - start_color[2]) * y / height)
|
||||
a = int(start_color[3] + (end_color[3] - start_color[3]) * y / height)
|
||||
|
||||
# 绘制当前行的渐变色
|
||||
draw.line((0, y, width, y), fill=(r, g, b, a))
|
||||
|
||||
# Create a mask from the image's alpha channel
|
||||
mask = image.split()[-1]
|
||||
|
||||
# Convert the mask to a black and white image
|
||||
mask = mask.convert('L')
|
||||
|
||||
image=image.convert('RGB')
|
||||
|
||||
return (image, mask)
|
||||
|
||||
# 示例用法
|
||||
# width = 500
|
||||
# height = 200
|
||||
# start_color_hex = 'FF0000FF' # 红色,完全不透明
|
||||
# end_color_hex = '0000FFFF' # 蓝色,完全不透明
|
||||
|
||||
# gradient_image = generate_gradient_image(width, height, start_color_hex, end_color_hex)
|
||||
# gradient_image.save('gradient_image.png')
|
||||
|
||||
def rgb_to_hex(rgb):
|
||||
r, g, b = rgb
|
||||
hex_color = "#{:02x}{:02x}{:02x}".format(r, g, b)
|
||||
return hex_color
|
||||
|
||||
|
||||
# 读取不了分层
|
||||
def load_psd(image):
|
||||
layers=[]
|
||||
@@ -254,10 +354,60 @@ def get_images_filepath(f,white_bg=False):
|
||||
|
||||
return images
|
||||
|
||||
|
||||
|
||||
def get_average_color_image(image):
|
||||
# 打开图片
|
||||
# image = Image.open(image_path)
|
||||
|
||||
# 将图片转换为RGB模式
|
||||
image = image.convert("RGB")
|
||||
|
||||
# 获取图片的像素值
|
||||
pixel_data = image.load()
|
||||
|
||||
# 初始化颜色总和和像素数量
|
||||
total_red = 0
|
||||
total_green = 0
|
||||
total_blue = 0
|
||||
pixel_count = 0
|
||||
|
||||
# 遍历图片的每个像素
|
||||
for i in range(image.width):
|
||||
for j in range(image.height):
|
||||
# 获取像素的RGB值
|
||||
r, g, b = pixel_data[i, j]
|
||||
|
||||
# 累加颜色值
|
||||
total_red += r
|
||||
total_green += g
|
||||
total_blue += b
|
||||
|
||||
# 像素数量加1
|
||||
pixel_count += 1
|
||||
|
||||
# 计算平均颜色值
|
||||
average_red = int(total_red // pixel_count)
|
||||
average_green = int(total_green // pixel_count)
|
||||
average_blue = int(total_blue // pixel_count)
|
||||
|
||||
# 返回平均颜色值
|
||||
|
||||
im = Image.new("RGB", (image.width, image.height), (average_red, average_green, average_blue))
|
||||
|
||||
hex=rgb_to_hex((average_red, average_green, average_blue))
|
||||
return (im,hex)
|
||||
|
||||
|
||||
|
||||
# 创建噪声图像
|
||||
def create_noisy_image(width, height, mode="RGB", noise_level=128):
|
||||
def create_noisy_image(width, height, mode="RGB", noise_level=128, background_color="#FFFFFF"):
|
||||
|
||||
background_rgb = tuple(int(background_color[i:i+2], 16) for i in (1, 3, 5))
|
||||
image = Image.new(mode, (width, height), background_rgb)
|
||||
|
||||
# 创建空白图像
|
||||
image = Image.new(mode, (width, height))
|
||||
# image = Image.new(mode, (width, height))
|
||||
|
||||
# 遍历每个像素,并随机设置像素值
|
||||
pixels = image.load()
|
||||
@@ -418,26 +568,44 @@ def merge_images(bg_image, layer_image, mask, x, y, width, height, scale_option)
|
||||
return bg_image
|
||||
|
||||
|
||||
def resize_image(layer_image,scale_option,width,height):
|
||||
# TODO 几个像素点的底
|
||||
def resize_image(layer_image, scale_option, width, height,color="white"):
|
||||
layer_image = layer_image.convert("RGB")
|
||||
original_width, original_height = layer_image.size
|
||||
|
||||
if scale_option == "height":
|
||||
# 按照高度比例缩放
|
||||
original_width, original_height = layer_image.size
|
||||
# Scale image based on height
|
||||
scale = height / original_height
|
||||
new_width = int(original_width * scale)
|
||||
layer_image = layer_image.resize((new_width, height))
|
||||
|
||||
elif scale_option == "width":
|
||||
# 按照宽度比例缩放
|
||||
original_width, original_height = layer_image.size
|
||||
# Scale image based on width
|
||||
scale = width / original_width
|
||||
new_height = int(original_height * scale)
|
||||
layer_image = layer_image.resize((width, new_height))
|
||||
|
||||
elif scale_option == "overall":
|
||||
# 整体缩放
|
||||
# Scale image overall
|
||||
layer_image = layer_image.resize((width, height))
|
||||
|
||||
elif scale_option == "center":
|
||||
# Scale image to minimum of width and height, center it, and fill extra area with black
|
||||
scale = min(width / original_width, height / original_height)
|
||||
new_width = math.ceil(original_width * scale)
|
||||
new_height = math.ceil(original_height * scale)
|
||||
resized_image = Image.new("RGB", (width, height), color=color)
|
||||
resized_image.paste(layer_image.resize((new_width, new_height)), ((width - new_width) // 2, (height - new_height) // 2))
|
||||
resized_image = resized_image.convert("RGB")
|
||||
return resized_image
|
||||
|
||||
return layer_image
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# def generate_text_image(text_list, font_path, font_size, text_color, vertical=True, spacing=0):
|
||||
# # Load Chinese font
|
||||
# font = ImageFont.truetype(font_path, font_size)
|
||||
@@ -477,8 +645,7 @@ def resize_image(layer_image,scale_option,width,height):
|
||||
# image=image.convert('RGB')
|
||||
|
||||
# return (image,alpha_image)
|
||||
|
||||
def generate_text_image(text, font_path, font_size, text_color, vertical=True, spacing=0):
|
||||
def generate_text_image(text, font_path, font_size, text_color, vertical=True, stroke=False, stroke_color=(0, 0, 0), stroke_width=1, spacing=0):
|
||||
# Split text into lines based on line breaks
|
||||
lines = text.split("\n")
|
||||
|
||||
@@ -494,19 +661,17 @@ def generate_text_image(text, font_path, font_size, text_color, vertical=True, s
|
||||
x = 0
|
||||
y = 0
|
||||
for i in range(len(lines)):
|
||||
line=lines[i]
|
||||
line = lines[i]
|
||||
for char in line:
|
||||
char_coordinates.append((x, y))
|
||||
y += font_size + spacing
|
||||
x += font_size + spacing
|
||||
y = 0
|
||||
# print(char_coordinates)
|
||||
else:
|
||||
x = 0
|
||||
y = 0
|
||||
for line in lines:
|
||||
for char in line:
|
||||
#print('char',char)
|
||||
char_coordinates.append((x, y))
|
||||
x += font_size + spacing
|
||||
y += font_size + spacing
|
||||
@@ -515,35 +680,42 @@ def generate_text_image(text, font_path, font_size, text_color, vertical=True, s
|
||||
# 3. Calculate image width and height
|
||||
if layout == "vertical":
|
||||
width = (len(lines) * (font_size + spacing)) - spacing
|
||||
height = ((len(max(lines, key=len))+1) * (font_size + spacing)) + spacing
|
||||
height = ((len(max(lines, key=len)) + 1) * (font_size + spacing)) + spacing
|
||||
else:
|
||||
width = (len(max(lines, key=len)) * (font_size + spacing)) - spacing
|
||||
height = ((len(lines)-1) * (font_size + spacing)) + font_size
|
||||
height = ((len(lines) - 1) * (font_size + spacing)) + font_size
|
||||
|
||||
# 4. Draw each character on the image
|
||||
image = Image.new('RGBA', (width, height), (255, 255, 255,0))
|
||||
image = Image.new('RGBA', (width, height), (255, 255, 255, 0))
|
||||
draw = ImageDraw.Draw(image)
|
||||
font = ImageFont.truetype(font_path, font_size)
|
||||
|
||||
index=0
|
||||
|
||||
index = 0
|
||||
for i, line in enumerate(lines):
|
||||
for j, char in enumerate(line):
|
||||
x, y = char_coordinates[index]
|
||||
|
||||
if stroke:
|
||||
draw.text((x-stroke_width, y), char, font=font, fill=stroke_color)
|
||||
draw.text((x+stroke_width, y), char, font=font, fill=stroke_color)
|
||||
draw.text((x, y-stroke_width), char, font=font, fill=stroke_color)
|
||||
draw.text((x, y+stroke_width), char, font=font, fill=stroke_color)
|
||||
|
||||
draw.text((x, y), char, font=font, fill=text_color)
|
||||
index+=1
|
||||
index += 1
|
||||
|
||||
# image.save(output_image_path)
|
||||
|
||||
# 分离alpha通道
|
||||
# Separate alpha channel
|
||||
alpha_channel = image.split()[3]
|
||||
|
||||
# 创建一个只有alpha通道的新图像
|
||||
# Create a new image with only the alpha channel
|
||||
alpha_image = Image.new('L', image.size)
|
||||
alpha_image.putdata(alpha_channel.getdata())
|
||||
|
||||
image=image.convert('RGB')
|
||||
image = image.convert('RGB')
|
||||
|
||||
return (image, alpha_image)
|
||||
|
||||
|
||||
return (image,alpha_image)
|
||||
|
||||
def base64_to_image(base64_string):
|
||||
# 去除前缀
|
||||
@@ -606,7 +778,7 @@ class SmoothMask:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/mask"
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
|
||||
@@ -654,7 +826,7 @@ class FeatheredMask:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/mask"
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
@@ -721,7 +893,7 @@ class SplitLongMask:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/mask"
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
@@ -765,7 +937,7 @@ class TransparentImage:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# INPUT_IS_LIST = True, 一个batch传进来
|
||||
OUTPUT_IS_LIST = (True,True,True,)
|
||||
@@ -814,7 +986,10 @@ class TransparentImage:
|
||||
# result 里输出给下个节点的数据
|
||||
# print('TransparentImage',len(images_rgb))
|
||||
return {"ui":{"images": ui_images,"image_paths":image_paths},"result": (image_paths,images_rgb,images_rgba)}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class EnhanceImage:
|
||||
@classmethod
|
||||
@@ -834,22 +1009,29 @@ class EnhanceImage:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
# 运行的函数
|
||||
def run(self,image,contrast):
|
||||
# print('EnhanceImage',image.shape)
|
||||
image=tensor2pil(image)
|
||||
|
||||
image=enhance_depth_map(image,contrast)
|
||||
# print('EnhanceImage',len(image),image[0].shape)
|
||||
contrast=contrast[0]
|
||||
res=[]
|
||||
for ims in image:
|
||||
for im in ims:
|
||||
|
||||
image=pil2tensor(image)
|
||||
image=tensor2pil(im)
|
||||
|
||||
image=enhance_depth_map(image,contrast)
|
||||
|
||||
image=pil2tensor(image)
|
||||
|
||||
res.append(image)
|
||||
|
||||
return (image,)
|
||||
return (res,)
|
||||
|
||||
|
||||
|
||||
@@ -870,7 +1052,7 @@ class LoadImagesFromPath:
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"file_path": ("STRING",{"multiline": False,"default": ""}),
|
||||
"file_path": ("STRING",{"multiline": False,"default": "","dynamicPrompts": False}),
|
||||
},
|
||||
"optional":{
|
||||
"white_bg": (["disable","enable"],),
|
||||
@@ -893,7 +1075,7 @@ class LoadImagesFromPath:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,True,False,)
|
||||
@@ -919,7 +1101,7 @@ class LoadImagesFromPath:
|
||||
if watcher_folder!=None:
|
||||
watcher_folder.stop()
|
||||
|
||||
|
||||
#TODO 修bug: ps6477. tmp
|
||||
images=get_images_filepath(file_path,white_bg=='enable')
|
||||
|
||||
# 当开启了监听,则取最新的,第一个文件
|
||||
@@ -947,7 +1129,7 @@ class LoadImagesFromPath:
|
||||
print("发生了一个未知的错误:", str(e))
|
||||
|
||||
# print('#prompt::::',prompt)
|
||||
return (imgs,masks,prompt,)
|
||||
return {"ui": {"seed": [1]}, "result":(imgs,masks,prompt,)}
|
||||
|
||||
|
||||
# TODO 扩大选区的功能,重新输出mask
|
||||
@@ -958,29 +1140,81 @@ class ImageCropByAlpha:
|
||||
"RGBA": ("RGBA",), },
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
# RETURN_NAMES = ("WIDTH","HEIGHT","X","Y",)
|
||||
RETURN_TYPES = ("IMAGE","MASK","MASK","INT","INT","INT","INT",)
|
||||
RETURN_NAMES = ("IMAGE","MASK","AREA_MASK","x","y","width","height",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,True,True,True,True,True,True,)
|
||||
|
||||
def run(self,image,RGBA):
|
||||
# print(image.shape,RGBA.shape)
|
||||
|
||||
image=image[0]
|
||||
RGBA=RGBA[0]
|
||||
|
||||
bf_im = tensor2pil(image)
|
||||
|
||||
# print(RGBA)
|
||||
im=tensor2pil(RGBA)
|
||||
im=naive_cutout(im,im)
|
||||
x, y, w, h=get_not_transparent_area(im)
|
||||
print('#ForImageCrop:',w, h,x, y,)
|
||||
# print('#ForImageCrop:',w, h,x, y,)
|
||||
|
||||
x = min(x, image.shape[2] - 1)
|
||||
y = min(y, image.shape[1] - 1)
|
||||
to_x = w + x
|
||||
to_y = h + y
|
||||
|
||||
x_1=x
|
||||
y_1=y
|
||||
width_1=w
|
||||
height_1=h
|
||||
|
||||
img = image[:,y:to_y, x:to_x, :]
|
||||
return (img,)
|
||||
|
||||
|
||||
# 原图的mask
|
||||
ori=RGBA[:,y:to_y, x:to_x, :]
|
||||
ori=tensor2pil(ori)
|
||||
|
||||
# 创建一个新的图像对象,大小和模式与原始图像相同
|
||||
new_image = Image.new("RGBA", ori.size)
|
||||
|
||||
# 获取原始图像的像素数据
|
||||
pixel_data = ori.load()
|
||||
|
||||
# 获取新图像的像素数据
|
||||
new_pixel_data = new_image.load()
|
||||
|
||||
# 遍历图像的每个像素
|
||||
for y in range(ori.size[1]):
|
||||
for x in range(ori.size[0]):
|
||||
# 获取当前像素的RGBA值
|
||||
r, g, b, a = pixel_data[x, y]
|
||||
|
||||
# 如果a通道不为0(不透明),将当前像素设置为白色
|
||||
if a != 0:
|
||||
new_pixel_data[x, y] = (255, 255, 255, 255)
|
||||
else:
|
||||
new_pixel_data[x, y] = (r, g, b, a)
|
||||
|
||||
# 保存修改后的图像
|
||||
# new_image.save("output.png")
|
||||
|
||||
ori=new_image.convert('L')
|
||||
# threshold = 128
|
||||
# ori = ori.point(lambda x: 0 if x < threshold else 255, '1')
|
||||
ori=pil2tensor(ori)
|
||||
|
||||
# 矩形区域,mask
|
||||
b_image =AreaToMask_run(RGBA)
|
||||
# img=None
|
||||
# b_image=None
|
||||
return ([img],[ori],[b_image],[x_1],[y_1],[width_1],[height_1],)
|
||||
|
||||
|
||||
|
||||
@@ -1009,6 +1243,7 @@ class TextImage:
|
||||
}),
|
||||
"text_color":("STRING",{"multiline": False,"default": "#000000","dynamicPrompts": False}),
|
||||
"vertical":("BOOLEAN", {"default": True},),
|
||||
"stroke":("BOOLEAN", {"default": False},),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1017,16 +1252,16 @@ class TextImage:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,False,)
|
||||
|
||||
def run(self,text,font_path,font_size,spacing,text_color,vertical):
|
||||
def run(self,text,font_path,font_size,spacing,text_color,vertical,stroke):
|
||||
|
||||
# text_list=list(text)
|
||||
|
||||
img,mask=generate_text_image(text,font_path,font_size,text_color,vertical,spacing)
|
||||
# stroke=False, stroke_color=(0, 0, 0), stroke_width=1, spacing=0
|
||||
img,mask=generate_text_image(text,font_path,font_size,text_color,vertical,stroke,(0, 0, 0),1,spacing)
|
||||
|
||||
img=pil2tensor(img)
|
||||
mask=pil2tensor(mask)
|
||||
@@ -1046,7 +1281,7 @@ class LoadImagesFromURL:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (True,True,)
|
||||
@@ -1103,7 +1338,7 @@ class SvgImage:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,True,)
|
||||
@@ -1137,7 +1372,7 @@ class Image3D:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,False,False,False,)
|
||||
@@ -1177,6 +1412,24 @@ class Image3D:
|
||||
|
||||
|
||||
|
||||
def AreaToMask_run(RGBA):
|
||||
# print(RGBA)
|
||||
im=tensor2pil(RGBA)
|
||||
im=naive_cutout(im,im)
|
||||
x, y, w, h=get_not_transparent_area(im)
|
||||
|
||||
im=im.convert("RGBA")
|
||||
# print('#AreaToMask:',im)
|
||||
img=areaToMask(x,y,w,h,im)
|
||||
img=img.convert("RGBA")
|
||||
mask=pil2tensor(img)
|
||||
|
||||
channels = ["red", "green", "blue", "alpha"]
|
||||
# print(mask,mask.shape)
|
||||
mask = mask[:, :, :, channels.index("green")]
|
||||
|
||||
return mask
|
||||
|
||||
|
||||
class AreaToMask:
|
||||
@classmethod
|
||||
@@ -1189,26 +1442,14 @@ class AreaToMask:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/mask"
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,RGBA):
|
||||
# print(RGBA)
|
||||
im=tensor2pil(RGBA)
|
||||
im=naive_cutout(im,im)
|
||||
x, y, w, h=get_not_transparent_area(im)
|
||||
|
||||
im=im.convert("RGBA")
|
||||
# print('#AreaToMask:',im)
|
||||
img=areaToMask(x,y,w,h,im)
|
||||
img=img.convert("RGBA")
|
||||
mask=pil2tensor(img)
|
||||
|
||||
channels = ["red", "green", "blue", "alpha"]
|
||||
# print(mask,mask.shape)
|
||||
mask = mask[:, :, :, channels.index("green")]
|
||||
mask =AreaToMask_run(RGBA)
|
||||
|
||||
return (mask,)
|
||||
|
||||
@@ -1224,7 +1465,7 @@ class FaceToMask:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/mask"
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
@@ -1269,7 +1510,7 @@ class EmptyLayer:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/layer"
|
||||
CATEGORY = "♾️Mixlab/Layer"
|
||||
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
@@ -1350,7 +1591,7 @@ class NewLayer:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/layer"
|
||||
CATEGORY = "♾️Mixlab/Layer"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
@@ -1438,7 +1679,7 @@ class ShowLayer:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/layer"
|
||||
CATEGORY = "♾️Mixlab/Layer"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
# OUTPUT_IS_LIST = (True,)
|
||||
@@ -1477,12 +1718,12 @@ class MergeLayers:
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
# RETURN_NAMES = ("WIDTH","HEIGHT","X","Y",)
|
||||
RETURN_TYPES = ("IMAGE","MASK",)
|
||||
RETURN_NAMES = ("IMAGE","MASK",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/layer"
|
||||
CATEGORY = "♾️Mixlab/Layer"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
# OUTPUT_IS_LIST = (False,)
|
||||
@@ -1503,7 +1744,10 @@ class MergeLayers:
|
||||
bg_image=tensor2pil(bg_image)
|
||||
# 按z-index排序
|
||||
layers_new = sorted(layers, key=lambda x: x["z_index"])
|
||||
|
||||
|
||||
width, height = bg_image.size
|
||||
final_mask= Image.new('L', (width, height), 0)
|
||||
|
||||
for layer in layers_new:
|
||||
image=layer['image']
|
||||
mask=layer['mask']
|
||||
@@ -1529,24 +1773,105 @@ class MergeLayers:
|
||||
layer['scale_option']
|
||||
)
|
||||
|
||||
mask=bg_image.convert('RGBA')
|
||||
mask=pil2tensor(mask)
|
||||
final_mask=merge_images(final_mask,
|
||||
layer_mask.convert('RGB'),
|
||||
layer_mask,
|
||||
layer['x'],
|
||||
layer['y'],
|
||||
layer['width'],
|
||||
layer['height'],
|
||||
layer['scale_option']
|
||||
)
|
||||
|
||||
final_mask=final_mask.convert('L')
|
||||
|
||||
# mask=bg_image.convert('RGBA')
|
||||
final_mask=pil2tensor(final_mask)
|
||||
|
||||
bg_image=bg_image.convert('RGB')
|
||||
bg_image=pil2tensor(bg_image)
|
||||
|
||||
channels = ["red", "green", "blue", "alpha"]
|
||||
# print(mask,mask.shape)
|
||||
mask = mask[:, :, :, channels.index("green")]
|
||||
|
||||
bg_images.append(bg_image)
|
||||
masks.append(mask)
|
||||
masks.append(final_mask)
|
||||
|
||||
bg_images=torch.cat(bg_images, dim=0)
|
||||
masks=torch.cat(masks, dim=0)
|
||||
return (bg_images,masks,)
|
||||
|
||||
|
||||
class GradientImage:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"width": ("INT",{
|
||||
"default": 512,
|
||||
"min": 1, # 最小值
|
||||
"max": 8192, # 最大值
|
||||
"step": 1, # 间隔
|
||||
"display": "number" # 控件类型: 输入框 number、滑块 slider
|
||||
}),
|
||||
"height": ("INT",{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"start_color_hex": ("STRING",{"multiline": False,"default": "#FFFFFF","dynamicPrompts": False}),
|
||||
"end_color_hex": ("STRING",{"multiline": False,"default": "#000000","dynamicPrompts": False}),
|
||||
},
|
||||
}
|
||||
|
||||
# 输出的数据类型
|
||||
RETURN_TYPES = ("IMAGE","MASK",)
|
||||
|
||||
# 运行时方法名称
|
||||
FUNCTION = "run"
|
||||
|
||||
# 右键菜单目录
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# 输入是否为列表
|
||||
INPUT_IS_LIST = False
|
||||
|
||||
# 输出是否为列表
|
||||
OUTPUT_IS_LIST = (False,False,)
|
||||
|
||||
def run(self,width,height,start_color_hex, end_color_hex):
|
||||
|
||||
im,mask=generate_gradient_image(width, height, start_color_hex, end_color_hex)
|
||||
|
||||
#获取临时目录:temp
|
||||
output_dir = folder_paths.get_temp_directory()
|
||||
|
||||
(
|
||||
full_output_folder,
|
||||
filename,
|
||||
counter,
|
||||
subfolder,
|
||||
_,
|
||||
) = folder_paths.get_save_image_path('tmp_', output_dir)
|
||||
|
||||
image_file = f"{filename}_{counter:05}.png"
|
||||
|
||||
image_path=os.path.join(full_output_folder, image_file)
|
||||
# 保存图片
|
||||
im.save(image_path,compress_level=6)
|
||||
|
||||
# 把PIL数据类型转为tensor
|
||||
im=pil2tensor(im)
|
||||
|
||||
mask=pil2tensor(mask)
|
||||
|
||||
# 定义ui字段,数据将回传到web前端的 nodeType.prototype.onExecuted
|
||||
# result是节点的输出
|
||||
return {"ui":{"images": [{
|
||||
"filename": image_file,
|
||||
"subfolder": subfolder,
|
||||
"type":"temp"
|
||||
}]},"result": (im,mask,)}
|
||||
|
||||
|
||||
|
||||
class NoiseImage:
|
||||
@classmethod
|
||||
@@ -1573,7 +1898,7 @@ class NoiseImage:
|
||||
"step": 1,
|
||||
"display": "slider"
|
||||
}),
|
||||
|
||||
"color_hex": ("STRING",{"multiline": False,"default": "#FFFFFF","dynamicPrompts": False}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1584,7 +1909,7 @@ class NoiseImage:
|
||||
FUNCTION = "run"
|
||||
|
||||
# 右键菜单目录
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# 输入是否为列表
|
||||
INPUT_IS_LIST = False
|
||||
@@ -1592,9 +1917,9 @@ class NoiseImage:
|
||||
# 输出是否为列表
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,width,height,noise_level):
|
||||
def run(self,width,height,noise_level,color_hex):
|
||||
# 创建噪声图像
|
||||
im=create_noisy_image(width,height,"RGB",noise_level)
|
||||
im=create_noisy_image(width,height,"RGB",noise_level,color_hex)
|
||||
|
||||
#获取临时目录:temp
|
||||
output_dir = folder_paths.get_temp_directory()
|
||||
@@ -1643,38 +1968,164 @@ class ResizeImage:
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
"scale_option": (["width","height",'overall'],),
|
||||
"scale_option": (["width","height",'overall','center'],),
|
||||
|
||||
},
|
||||
|
||||
"optional":{
|
||||
"image": ("IMAGE",),
|
||||
"average_color": (["on",'off'],),
|
||||
"fill_color":("STRING",{"multiline": False,"default": "#FFFFFF","dynamicPrompts": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_TYPES = ("IMAGE","IMAGE","STRING",)
|
||||
RETURN_NAMES = ("image","average_image","average_hex",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
OUTPUT_IS_LIST = (True,True,True,)
|
||||
|
||||
def run(self,width,height,scale_option,image=None):
|
||||
def run(self,width,height,scale_option,image=None,average_color=['on'],fill_color=["#FFFFFF"]):
|
||||
|
||||
w=width[0]
|
||||
h=height[0]
|
||||
scale_option=scale_option[0]
|
||||
average_color=average_color[0]
|
||||
fill_color=fill_color[0]
|
||||
|
||||
imgs=[]
|
||||
average_images=[]
|
||||
hexs=[]
|
||||
|
||||
if image==None:
|
||||
im=create_noisy_image(w,h,"RGB")
|
||||
else:
|
||||
im=image[0]
|
||||
im=tensor2pil(im)
|
||||
im=resize_image(im,scale_option,w,h)
|
||||
im=im.convert('RGB')
|
||||
a_im,hex=get_average_color_image(im)
|
||||
|
||||
im=pil2tensor(im)
|
||||
imgs.append(im)
|
||||
|
||||
im=pil2tensor(im)
|
||||
a_im=pil2tensor(a_im)
|
||||
average_images.append(a_im)
|
||||
hexs.append(hex)
|
||||
else:
|
||||
for ims in image:
|
||||
for im in ims:
|
||||
im=tensor2pil(im)
|
||||
im=resize_image(im,scale_option,w,h,fill_color)
|
||||
im=im.convert('RGB')
|
||||
|
||||
a_im,hex=get_average_color_image(im)
|
||||
|
||||
im=pil2tensor(im)
|
||||
imgs.append(im)
|
||||
|
||||
a_im=pil2tensor(a_im)
|
||||
average_images.append(a_im)
|
||||
hexs.append(hex)
|
||||
|
||||
return (im,)
|
||||
return (imgs,average_images,hexs,)
|
||||
|
||||
|
||||
class MirroredImage:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"image": ("IMAGE",),
|
||||
},
|
||||
}
|
||||
|
||||
# 输出的数据类型
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
|
||||
# 运行时方法名称
|
||||
FUNCTION = "run"
|
||||
|
||||
# 右键菜单目录
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# 输入是否为列表
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
# 输出是否为列表
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,image):
|
||||
res=[]
|
||||
for ims in image:
|
||||
for im in ims:
|
||||
img=tensor2pil(im)
|
||||
mirrored_image = img.transpose(Image.FLIP_LEFT_RIGHT)
|
||||
img=pil2tensor(mirrored_image)
|
||||
res.append(img)
|
||||
return (res,)
|
||||
|
||||
|
||||
|
||||
class GetImageSize_:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("width", "height")
|
||||
|
||||
FUNCTION = "get_size"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
def get_size(self, image):
|
||||
_, height, width, _ = image.shape
|
||||
return (width, height)
|
||||
|
||||
|
||||
|
||||
class ImageColorTransfer:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"source": ("IMAGE",),
|
||||
"target": ("IMAGE",),
|
||||
},
|
||||
}
|
||||
|
||||
# 输出的数据类型
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
|
||||
# 运行时方法名称
|
||||
FUNCTION = "run"
|
||||
|
||||
# 右键菜单目录
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# 输入是否为列表
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
# 输出是否为列表
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,source,target):
|
||||
|
||||
res=[]
|
||||
|
||||
target=target[0][0]
|
||||
print(target.shape)
|
||||
target=tensor2pil(target)
|
||||
|
||||
for ims in source:
|
||||
for im in ims:
|
||||
image=tensor2pil(im)
|
||||
image=color_transfer(image,target)
|
||||
image=pil2tensor(image)
|
||||
res.append(image)
|
||||
|
||||
return (res,)
|
||||
|
||||
|
||||
|
||||
+47
-12
@@ -1,21 +1,54 @@
|
||||
import os
|
||||
import os,sys
|
||||
import folder_paths
|
||||
from simple_lama_inpainting import SimpleLama
|
||||
from PIL import Image
|
||||
|
||||
from PIL import Image
|
||||
import importlib.util
|
||||
import numpy as np
|
||||
|
||||
import torch
|
||||
|
||||
global _available
|
||||
_available=False
|
||||
|
||||
os.environ['LAMA_MODEL'] = os.path.join(folder_paths.models_dir, "lama/big-lama.pt")
|
||||
if os.environ.get("LAMA_MODEL"):
|
||||
model_path=os.environ.get("LAMA_MODEL")
|
||||
if not os.path.exists(model_path):
|
||||
os.environ['LAMA_MODEL']=''
|
||||
raise FileNotFoundError(
|
||||
f"lama torchscript model not found: {model_path}"
|
||||
)
|
||||
def is_installed(package):
|
||||
try:
|
||||
spec = importlib.util.find_spec(package)
|
||||
except ModuleNotFoundError:
|
||||
return False
|
||||
return spec is not None
|
||||
|
||||
if is_installed('simple_lama_inpainting')==False:
|
||||
import subprocess
|
||||
from packaging import version
|
||||
|
||||
if version.parse(torch.__version__)>=version.parse('2.1'):
|
||||
# 安装
|
||||
print('#pip install simple_lama_inpainting')
|
||||
|
||||
result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', 'simple_lama_inpainting'], capture_output=True, text=True)
|
||||
|
||||
#检查命令执行结果
|
||||
if result.returncode == 0:
|
||||
print("#install success")
|
||||
from simple_lama_inpainting import SimpleLama
|
||||
_available=True
|
||||
else:
|
||||
print("#install error")
|
||||
else:
|
||||
print('#pls check your torch version >= 2.1')
|
||||
|
||||
else:
|
||||
from simple_lama_inpainting import SimpleLama
|
||||
_available=True
|
||||
|
||||
|
||||
|
||||
llma_model_path=os.path.join(folder_paths.models_dir, "lama/big-lama.pt")
|
||||
if not os.path.exists(llma_model_path):
|
||||
os.environ['LAMA_MODEL']=''
|
||||
print(f"## lama torchscript model not found: {llma_model_path},pls download from https://github.com/enesmsahin/simple-lama-inpainting/releases/download/v0.1.0/big-lama.pt")
|
||||
else:
|
||||
os.environ['LAMA_MODEL'] = llma_model_path
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
@@ -39,6 +72,8 @@ def pil2tensor(image):
|
||||
|
||||
|
||||
class LaMaInpainting:
|
||||
global _available
|
||||
available=_available
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
@@ -53,7 +88,7 @@ class LaMaInpainting:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
+288
-57
@@ -1,15 +1,16 @@
|
||||
import random
|
||||
import comfy.utils
|
||||
import json
|
||||
import os
|
||||
import numpy as np
|
||||
from urllib import request, parse
|
||||
|
||||
|
||||
|
||||
def queue_prompt(prompt_workflow):
|
||||
p = {"prompt": prompt_workflow}
|
||||
data = json.dumps(p).encode('utf-8')
|
||||
req = request.Request("http://127.0.0.1:8188/prompt", data=data)
|
||||
request.urlopen(req)
|
||||
import folder_paths
|
||||
from PIL import Image, ImageOps,ImageFilter,ImageEnhance,ImageDraw,ImageSequence, ImageFont
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
# def queue_prompt(prompt_workflow):
|
||||
# p = {"prompt": prompt_workflow}
|
||||
# data = json.dumps(p).encode('utf-8')
|
||||
# req = request.Request("http://127.0.0.1:8188/prompt", data=data)
|
||||
# request.urlopen(req)
|
||||
|
||||
|
||||
default_prompt1='''Swing
|
||||
@@ -45,6 +46,232 @@ default_prompt1='''Swing
|
||||
'''
|
||||
default_prompt1="\n".join([p.strip() for p in default_prompt1.split('\n') if p.strip()!=''])
|
||||
|
||||
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
def addWeight(text, weight=1):
|
||||
if weight == 1:
|
||||
return text
|
||||
else:
|
||||
return f"({text}:{round(weight,2)})"
|
||||
|
||||
def prompt_delete_words(sentence, new_words_length):
|
||||
# 使用逗号分割句子,并去除空格
|
||||
words = [word.strip() for word in sentence.split(",")]
|
||||
|
||||
# 计算需要删除的单词数量
|
||||
num_to_delete = len(words) - new_words_length
|
||||
|
||||
words_to=[w for w in words]
|
||||
|
||||
# 逐个删除单词并存储在新列表中
|
||||
new_words = []
|
||||
for i in range(len(words)):
|
||||
if num_to_delete > 0:
|
||||
num_to_delete -= 1
|
||||
else:
|
||||
words_to.pop()
|
||||
if len(words_to)>0:
|
||||
new_words.append(", ".join(words_to))
|
||||
|
||||
return new_words
|
||||
|
||||
# # 测试方法
|
||||
# sentence = "a computer, a glass tablet with a keyboard on a dark background, 3d illustration, reflection, cgi 8k, clear glass, archaic, cut-away, white outline"
|
||||
# new_words_length = 5
|
||||
# result = prompt_delete_words(sentence, new_words_length)
|
||||
# print(result)
|
||||
|
||||
class PromptImage:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.prefix_append = "PromptImage"
|
||||
self.compress_level = 4
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"prompts": ("STRING",
|
||||
{
|
||||
"multiline": True,
|
||||
"default": '',
|
||||
"dynamicPrompts": False
|
||||
}),
|
||||
|
||||
"images": ("IMAGE",{"default": None}),
|
||||
"save_to_image": (["enable", "disable"],),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
# 运行的函数
|
||||
def run(self,prompts,images,save_to_image):
|
||||
filename_prefix="mixlab_"
|
||||
filename_prefix += self.prefix_append
|
||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(
|
||||
filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0])
|
||||
|
||||
results = list()
|
||||
|
||||
save_to_image=save_to_image[0]=='enable'
|
||||
|
||||
for index in range(len(images)):
|
||||
res=[]
|
||||
imgs=images[index]
|
||||
|
||||
for image in imgs:
|
||||
img=tensor2pil(image)
|
||||
|
||||
metadata = None
|
||||
if save_to_image:
|
||||
metadata = PngInfo()
|
||||
prompt_text=prompts[index]
|
||||
if prompt_text is not None:
|
||||
metadata.add_text("prompt_text", prompt_text)
|
||||
|
||||
file = f"{filename}_{index}_{counter:05}_.png"
|
||||
img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=self.compress_level)
|
||||
res.append({
|
||||
"filename": file,
|
||||
"subfolder": subfolder,
|
||||
"type": self.type
|
||||
})
|
||||
counter += 1
|
||||
results.append(res)
|
||||
|
||||
return { "ui": { "_images": results,"prompts":prompts } }
|
||||
|
||||
|
||||
|
||||
|
||||
class PromptSimplification:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"prompt": ("STRING",
|
||||
{
|
||||
"multiline": True,
|
||||
"default": '',
|
||||
"dynamicPrompts": False
|
||||
}),
|
||||
|
||||
"length":("INT", {"default": 5, "min": 1,"max":100, "step": 1, "display": "number"}),
|
||||
|
||||
# "min_value":("FLOAT", {
|
||||
# "default": -2,
|
||||
# "min": -10,
|
||||
# "max": 0xffffffffffffffff,
|
||||
# "step": 0.01,
|
||||
# "display": "number"
|
||||
# }),
|
||||
# "max_value":("FLOAT", {
|
||||
# "default": 2,
|
||||
# "min": -10,
|
||||
# "max": 0xffffffffffffffff,
|
||||
# "step": 0.01,
|
||||
# "display": "number"
|
||||
# }),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompts",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
OUTPUT_NODE = True
|
||||
|
||||
# 运行的函数
|
||||
def run(self,prompt,length):
|
||||
length=length[0]
|
||||
result=[]
|
||||
for p in prompt:
|
||||
nps=prompt_delete_words(p,length)
|
||||
for n in nps:
|
||||
result.append(n)
|
||||
|
||||
result= [elem.strip() for elem in result if elem.strip()]
|
||||
|
||||
return {"ui": {"prompts": result}, "result": (result,)}
|
||||
|
||||
|
||||
|
||||
class PromptSlide:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
|
||||
"prompt_keyword": ("STRING",
|
||||
{
|
||||
"multiline": False,
|
||||
"default": '',
|
||||
"dynamicPrompts": False
|
||||
}),
|
||||
|
||||
"weight":("FLOAT", {"default": 1, "min": -3,"max": 3,
|
||||
"step": 0.01,
|
||||
"display": "slider"}),
|
||||
|
||||
# "min_value":("FLOAT", {
|
||||
# "default": -2,
|
||||
# "min": -10,
|
||||
# "max": 0xffffffffffffffff,
|
||||
# "step": 0.01,
|
||||
# "display": "number"
|
||||
# }),
|
||||
# "max_value":("FLOAT", {
|
||||
# "default": 2,
|
||||
# "min": -10,
|
||||
# "max": 0xffffffffffffffff,
|
||||
# "step": 0.01,
|
||||
# "display": "number"
|
||||
# }),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
OUTPUT_NODE = False
|
||||
|
||||
# 运行的函数
|
||||
def run(self,prompt_keyword,weight):
|
||||
# if weight < min_value:
|
||||
# weight= min_value
|
||||
# elif weight > max_value:
|
||||
# weight= max_value
|
||||
p=addWeight(prompt_keyword,weight)
|
||||
return (p,)
|
||||
|
||||
|
||||
|
||||
|
||||
class RandomPrompt:
|
||||
|
||||
'''
|
||||
@@ -79,7 +306,7 @@ class RandomPrompt:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/prompt"
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
OUTPUT_NODE = True
|
||||
@@ -87,7 +314,7 @@ class RandomPrompt:
|
||||
|
||||
# 运行的函数
|
||||
def run(self,max_count,mutable_prompt,immutable_prompt,random_sample):
|
||||
print('#运行的函数',mutable_prompt,immutable_prompt,max_count,random_sample)
|
||||
# print('#运行的函数',mutable_prompt,immutable_prompt,max_count,random_sample)
|
||||
|
||||
# Split the text into an array of words
|
||||
words1 = mutable_prompt.split("\n")
|
||||
@@ -106,6 +333,8 @@ class RandomPrompt:
|
||||
w1=w1.strip()
|
||||
for w2 in words2:
|
||||
w2=w2.strip()
|
||||
if '``' not in w2:
|
||||
w2=w2+',``'
|
||||
if w1!='' and w2!='':
|
||||
prompts.append(w2.replace('``', w1))
|
||||
pbar.update(1)
|
||||
@@ -119,6 +348,8 @@ class RandomPrompt:
|
||||
else:
|
||||
prompts = prompts[:min(max_count,len(prompts))]
|
||||
|
||||
prompts= [elem.strip() for elem in prompts if elem.strip()]
|
||||
|
||||
# return (new_prompt)
|
||||
return {"ui": {"prompts": prompts}, "result": (prompts,)}
|
||||
|
||||
@@ -126,62 +357,62 @@ class RandomPrompt:
|
||||
|
||||
|
||||
|
||||
class RunWorkflow:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"workflow": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ''
|
||||
}),
|
||||
"prompt": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ''
|
||||
}),
|
||||
"image": ("IMAGE",),
|
||||
"input_node": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ''
|
||||
}),
|
||||
"output_node": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ''
|
||||
}),
|
||||
},
|
||||
# class RunWorkflow:
|
||||
# @classmethod
|
||||
# def INPUT_TYPES(s):
|
||||
# return {
|
||||
# "required": {
|
||||
# "workflow": ("STRING", {
|
||||
# "multiline": False,
|
||||
# "default": ''
|
||||
# }),
|
||||
# "prompt": ("STRING", {
|
||||
# "multiline": False,
|
||||
# "default": ''
|
||||
# }),
|
||||
# "image": ("IMAGE",),
|
||||
# "input_node": ("STRING", {
|
||||
# "multiline": False,
|
||||
# "default": ''
|
||||
# }),
|
||||
# "output_node": ("STRING", {
|
||||
# "multiline": False,
|
||||
# "default": ''
|
||||
# }),
|
||||
# },
|
||||
|
||||
}
|
||||
# }
|
||||
|
||||
|
||||
|
||||
RETURN_TYPES = ("IMAGE","STRING",)
|
||||
# RETURN_TYPES = ("IMAGE","STRING",)
|
||||
|
||||
FUNCTION = "run"
|
||||
# FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/workflow"
|
||||
# CATEGORY = "♾️Mixlab/workflow"
|
||||
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
OUTPUT_NODE = True
|
||||
# OUTPUT_IS_LIST = (True,)
|
||||
# OUTPUT_NODE = True
|
||||
|
||||
|
||||
# 运行的函数
|
||||
def run(self,workflow,prompt,image,input_node,output_node):
|
||||
print('#运行的函数',prompt,image,input_node,output_node)
|
||||
workflow=json.loads(workflow)
|
||||
input_node=input_node.split(".")
|
||||
workflow[input_node[0]][input_node[1]][input_node[2]]=prompt
|
||||
# # 运行的函数
|
||||
# def run(self,workflow,prompt,image,input_node,output_node):
|
||||
# print('#运行的函数',prompt,image,input_node,output_node)
|
||||
# workflow=json.loads(workflow)
|
||||
# input_node=input_node.split(".")
|
||||
# workflow[input_node[0]][input_node[1]][input_node[2]]=prompt
|
||||
|
||||
workflow_new={}
|
||||
# 遍历,seed设为随机
|
||||
for key, value in workflow.items():
|
||||
if 'inputs' in value:
|
||||
if 'seed' in value['inputs']:
|
||||
value['inputs']['seed']= random.randint(1, 18446744073709551614)
|
||||
workflow_new[key]=value
|
||||
# workflow_new={}
|
||||
# # 遍历,seed设为随机
|
||||
# for key, value in workflow.items():
|
||||
# if 'inputs' in value:
|
||||
# if 'seed' in value['inputs']:
|
||||
# value['inputs']['seed']= random.randint(1, 18446744073709551614)
|
||||
# workflow_new[key]=value
|
||||
|
||||
queue_prompt(workflow_new)
|
||||
print('#运行的函数',workflow_new[input_node[0]])
|
||||
# queue_prompt(workflow_new)
|
||||
# print('#运行的函数',workflow_new[input_node[0]])
|
||||
|
||||
# return (new_prompt)
|
||||
return {"ui":{"images": []},"result": ([image],['text'],)}
|
||||
# # return (new_prompt)
|
||||
# return {"ui":{"images": []},"result": ([image],['text'],)}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ class ScreenShareNode:
|
||||
RETURN_NAMES = ("IMAGE","PROMPT","FLOAT","INT")
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (False,False,False,False)
|
||||
@@ -118,7 +118,7 @@ class FloatingVideo:
|
||||
OUTPUT_NODE = True
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# INPUT_IS_LIST = True
|
||||
# OUTPUT_IS_LIST = (False,False,)
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
from transformers import pipeline, set_seed,AutoTokenizer, AutoModelForSeq2SeqLM
|
||||
import random
|
||||
import re
|
||||
|
||||
import os,sys
|
||||
import folder_paths
|
||||
|
||||
# from PIL import Image
|
||||
# import importlib.util
|
||||
|
||||
import comfy.utils
|
||||
# import numpy as np
|
||||
import torch
|
||||
import random
|
||||
|
||||
global _available
|
||||
_available=True
|
||||
|
||||
|
||||
text_generator_model_path=os.path.join(folder_paths.models_dir, "prompt_generator/text2image-prompt-generator")
|
||||
if not os.path.exists(text_generator_model_path):
|
||||
print(f"## text_generator_model not found: {text_generator_model_path}, pls download from https://huggingface.co/succinctly/text2image-prompt-generator/tree/main")
|
||||
text_generator_model_path='succinctly/text2image-prompt-generator'
|
||||
|
||||
zh_en_model_path=os.path.join(folder_paths.models_dir, "prompt_generator/opus-mt-zh-en")
|
||||
if not os.path.exists(zh_en_model_path):
|
||||
print(f"## zh_en_model not found: {zh_en_model_path}, pls download from https://huggingface.co/Helsinki-NLP/opus-mt-zh-en/tree/main")
|
||||
zh_en_model_path='Helsinki-NLP/opus-mt-zh-en'
|
||||
|
||||
|
||||
|
||||
def translate(zh_en_tokenizer,zh_en_model,texts):
|
||||
with torch.no_grad():
|
||||
encoded = zh_en_tokenizer(texts, return_tensors="pt")
|
||||
encoded.to(zh_en_model.device)
|
||||
sequences = zh_en_model.generate(**encoded)
|
||||
return zh_en_tokenizer.batch_decode(sequences, skip_special_tokens=True)
|
||||
|
||||
input = "青春不能回头,所以青春没有终点。 ——《火影忍者》"
|
||||
# print(input, translate(input))
|
||||
|
||||
|
||||
def text_generate(text_pipe,input,seed=None):
|
||||
|
||||
if seed==None:
|
||||
seed = random.randint(100, 1000000)
|
||||
|
||||
set_seed(seed)
|
||||
|
||||
for count in range(6):
|
||||
sequences = text_pipe(input, max_length=random.randint(60, 90), num_return_sequences=8)
|
||||
list = []
|
||||
for sequence in sequences:
|
||||
line = sequence['generated_text'].strip()
|
||||
if line != input and len(line) > (len(input) + 4) and line.endswith((":", "-", "—")) is False:
|
||||
list.append(line)
|
||||
|
||||
result = "\n".join(list)
|
||||
result = re.sub('[^ ]+\.[^ ]+','', result)
|
||||
result = result.replace("<", "").replace(">", "")
|
||||
if result != "":
|
||||
return result
|
||||
if count == 5:
|
||||
return result
|
||||
|
||||
# input = "Youth can't turn back, so there's no end to youth."
|
||||
# print(input, text_generate(input))
|
||||
|
||||
|
||||
import re
|
||||
|
||||
def correct_prompt_syntax(prompt):
|
||||
|
||||
print("input prompt",prompt)
|
||||
corrected_elements = []
|
||||
# 处理成统一的英文标点
|
||||
prompt = prompt.replace('(', '(').replace(')', ')').replace(',', ',').replace(';', ',').replace('。', '.').replace(':',':')
|
||||
# 删除多余的空格
|
||||
prompt = re.sub(r'\s+', ' ', prompt).strip()
|
||||
|
||||
# 分词
|
||||
prompt_elements = prompt.split(',')
|
||||
|
||||
for element in prompt_elements:
|
||||
element = element.strip()
|
||||
|
||||
# 处理空元素
|
||||
if not element:
|
||||
continue
|
||||
|
||||
# 检查并处理圆括号、方括号、尖括号
|
||||
if element[0] in '([':
|
||||
corrected_element = balance_brackets(element, '(', ')') if element[0] == '(' else balance_brackets(element, '[', ']')
|
||||
elif element[0] == '<':
|
||||
corrected_element = balance_brackets(element, '<', '>')
|
||||
else:
|
||||
# 删除开头的右括号或右方括号
|
||||
corrected_element = element.lstrip(')]')
|
||||
|
||||
corrected_elements.append(corrected_element)
|
||||
|
||||
# 重组修正后的prompt
|
||||
corrected_prompt = ', '.join(corrected_elements)
|
||||
print("output prompt",corrected_prompt)
|
||||
return corrected_prompt
|
||||
|
||||
def balance_brackets(element, open_bracket, close_bracket):
|
||||
open_brackets_count = element.count(open_bracket)
|
||||
close_brackets_count = element.count(close_bracket)
|
||||
return element + close_bracket * (open_brackets_count - close_brackets_count)
|
||||
|
||||
# # 示例使用
|
||||
# test_prompt = "((middle-century castles)), [forsaken: 0.8], (mystery dragons: 1.3, mist forests, sunsets, quiet; (((dummy)), [fisting city: 0.5] background, radiant, soft and flavoured,] promising mountains, ((starry: 1.6), [[crowds], [middle-century castle: urban landscapes of the future: 0.5], [yellow: bright sun: 0.7], overlooking"
|
||||
# corrected_prompt = correct_prompt_syntax(test_prompt)
|
||||
# print(corrected_prompt)
|
||||
|
||||
|
||||
|
||||
class ChinesePrompt:
|
||||
|
||||
global _available
|
||||
available=_available
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"text": ("STRING",{"multiline": True,"default": "", "dynamicPrompts": False}),
|
||||
"generation": (["on","off"],{"default": "off"}),
|
||||
},
|
||||
|
||||
"optional":{
|
||||
"seed":("INT", {"default": 100, "min": 100, "max": 1000000}),
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
global text_pipe,zh_en_model,zh_en_tokenizer
|
||||
|
||||
text_pipe= None
|
||||
zh_en_model=None
|
||||
zh_en_tokenizer=None
|
||||
|
||||
def run(self,text,seed,generation):
|
||||
global text_pipe,zh_en_model,zh_en_tokenizer
|
||||
|
||||
seed=seed[0]
|
||||
generation=generation[0]
|
||||
|
||||
# 进度条
|
||||
pbar = comfy.utils.ProgressBar(len(text)+1)
|
||||
|
||||
text = correct_prompt_syntax(text[0])
|
||||
|
||||
if zh_en_model==None:
|
||||
zh_en_model = AutoModelForSeq2SeqLM.from_pretrained(zh_en_model_path).eval()
|
||||
zh_en_tokenizer = AutoTokenizer.from_pretrained(zh_en_model_path)
|
||||
|
||||
zh_en_model.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
# zh_en_tokenizer.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
text_pipe=pipeline('text-generation', model=text_generator_model_path,device="cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
# text_pipe.model.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
prompt_result=[]
|
||||
|
||||
# print('zh_en_model device',zh_en_model.device,text_pipe.model.device,torch.cuda.current_device() )
|
||||
en_text=translate(zh_en_tokenizer,zh_en_model,text)
|
||||
|
||||
zh_en_model.to('cpu')
|
||||
print("test en_text",en_text)
|
||||
# en_text.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
pbar.update(1)
|
||||
for t in en_text:
|
||||
if generation=='on':
|
||||
prompt =text_generate(text_pipe,t,seed)
|
||||
# 多条,还是单条
|
||||
lines = prompt.split("\n")
|
||||
longest_line = max(lines, key=len)
|
||||
# print(longest_line)
|
||||
prompt_result.append(longest_line)
|
||||
else:
|
||||
prompt_result.append(t)
|
||||
pbar.update(1)
|
||||
|
||||
text_pipe.model.to('cpu')
|
||||
prompt_result = [correct_prompt_syntax(prompt_result[0])]
|
||||
|
||||
return {
|
||||
"ui":{
|
||||
"prompt": prompt_result
|
||||
},
|
||||
"result": (prompt_result,)}
|
||||
|
||||
|
||||
class PromptGenerate:
|
||||
|
||||
global _available
|
||||
available=_available
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"text": ("STRING",{"multiline": True,"default": "", "dynamicPrompts": False}),
|
||||
},
|
||||
|
||||
"optional":{
|
||||
"multiple": (["off","on"],),
|
||||
"seed":("INT", {"default": 100, "min": 100, "max": 1000000}),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
global text_pipe
|
||||
|
||||
text_pipe= None
|
||||
#
|
||||
|
||||
def run(self,text,multiple,seed):
|
||||
global text_pipe
|
||||
|
||||
seed=seed[0]
|
||||
|
||||
multiple=multiple[0]
|
||||
|
||||
# 进度条
|
||||
pbar = comfy.utils.ProgressBar(len(text))
|
||||
|
||||
text_pipe=pipeline('text-generation', model=text_generator_model_path,device="cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
prompt_result=[]
|
||||
|
||||
for t in text:
|
||||
prompt =text_generate(text_pipe,t,seed)
|
||||
prompt = prompt.split("\n")
|
||||
if multiple=='off':
|
||||
prompt = [max(prompt, key=len)]
|
||||
|
||||
for p in prompt:
|
||||
prompt_result.append(p)
|
||||
pbar.update(1)
|
||||
|
||||
text_pipe.model.to('cpu')
|
||||
|
||||
return {
|
||||
"ui":{
|
||||
"prompt": prompt_result
|
||||
},
|
||||
"result": (prompt_result,)}
|
||||
+236
-61
@@ -1,10 +1,56 @@
|
||||
import os
|
||||
import re,random
|
||||
import os,platform
|
||||
import re,random,json
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
# FONT_PATH= os.path.abspath(os.path.join(os.path.dirname(__file__),'../assets/王汉宗颜楷体繁.ttf'))
|
||||
import folder_paths
|
||||
import matplotlib.font_manager as fm
|
||||
import torch
|
||||
|
||||
def recursive_search(directory, excluded_dir_names=None):
|
||||
if not os.path.isdir(directory):
|
||||
return [], {}
|
||||
|
||||
if excluded_dir_names is None:
|
||||
excluded_dir_names = []
|
||||
|
||||
result = []
|
||||
dirs = {directory: os.path.getmtime(directory)}
|
||||
for dirpath, subdirs, filenames in os.walk(directory, followlinks=True, topdown=True):
|
||||
subdirs[:] = [d for d in subdirs if d not in excluded_dir_names]
|
||||
for file_name in filenames:
|
||||
relative_path = os.path.relpath(os.path.join(dirpath, file_name), directory)
|
||||
result.append(relative_path)
|
||||
for d in subdirs:
|
||||
path = os.path.join(dirpath, d)
|
||||
dirs[path] = os.path.getmtime(path)
|
||||
return result, dirs
|
||||
|
||||
def filter_files_extensions(files, extensions):
|
||||
return sorted(list(filter(lambda a: os.path.splitext(a)[-1].lower() in extensions or len(extensions) == 0, files)))
|
||||
|
||||
|
||||
def get_system_font_path():
|
||||
ps=[]
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
ps.append(os.path.join(os.environ["WINDIR"], "Fonts"))
|
||||
elif system == "Darwin":
|
||||
ps.append(os.path.join("/Library", "Fonts"))
|
||||
elif system == "Linux":
|
||||
ps.append(os.path.join("/usr", "share", "fonts"))
|
||||
ps.append(os.path.join("/usr", "local", "share", "fonts"))
|
||||
ps=[p for p in ps if os.path.exists(p)]
|
||||
file_paths=[]
|
||||
for f in ps:
|
||||
result, dirs=recursive_search(f)
|
||||
for r in result:
|
||||
file_paths.append(r)
|
||||
file_paths=filter_files_extensions(file_paths,[".otf", ".ttf"])
|
||||
|
||||
return file_paths
|
||||
|
||||
|
||||
|
||||
# import json
|
||||
# import hashlib
|
||||
@@ -34,13 +80,13 @@ def create_temp_file(image):
|
||||
) = folder_paths.get_save_image_path('tmp', output_dir)
|
||||
|
||||
|
||||
image=tensor2pil(image)
|
||||
im=tensor2pil(image)
|
||||
|
||||
image_file = f"{filename}_{counter:05}.png"
|
||||
|
||||
image_path=os.path.join(full_output_folder, image_file)
|
||||
|
||||
image.save(image_path,compress_level=4)
|
||||
im.save(image_path,compress_level=4)
|
||||
|
||||
return [{
|
||||
"filename": image_file,
|
||||
@@ -60,14 +106,14 @@ def get_font_files(directory):
|
||||
|
||||
# 尝试获取系统字体
|
||||
try:
|
||||
font_paths = fm.findSystemFonts()
|
||||
for path in font_paths:
|
||||
font_paths = get_system_font_path()
|
||||
for file in font_paths:
|
||||
try:
|
||||
font_prop = fm.FontProperties(fname=path)
|
||||
font_name = font_prop.get_name()
|
||||
font_files[font_name] = path
|
||||
font_name = os.path.splitext(file)[0]
|
||||
font_path = file
|
||||
font_files[font_name] = os.path.abspath(font_path)
|
||||
except Exception as e:
|
||||
print(f"Error processing font {path}: {e}")
|
||||
print(f"Error processing font {file}: {e}")
|
||||
except Exception as e:
|
||||
print(f"Error finding system fonts: {e}")
|
||||
|
||||
@@ -79,6 +125,21 @@ font_files = get_font_files(r_directory)
|
||||
# print(font_files)
|
||||
|
||||
|
||||
def flatten_list(nested_list):
|
||||
flat_list = []
|
||||
for item in nested_list:
|
||||
if isinstance(item, list):
|
||||
flat_list.extend(flatten_list(item))
|
||||
else:
|
||||
if torch.is_tensor(item):
|
||||
print('item.shape',item.shape)
|
||||
for i in range(item.shape[0]):
|
||||
flat_list.append(item[i:i + 1, ...])
|
||||
else:
|
||||
flat_list.append(item)
|
||||
return flat_list
|
||||
|
||||
|
||||
class ColorInput:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@@ -93,7 +154,7 @@ class ColorInput:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,False,False,False,False,)
|
||||
@@ -122,13 +183,13 @@ class FontInput:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,False,)
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,font):
|
||||
|
||||
|
||||
return (font_files[font],)
|
||||
|
||||
class TextToNumber:
|
||||
@@ -152,7 +213,7 @@ class TextToNumber:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
@@ -178,10 +239,31 @@ class FloatSlider:
|
||||
"number":("FLOAT", {
|
||||
"default": 0,
|
||||
"min": 0, #Minimum value
|
||||
"max": 1, #Maximum value
|
||||
"max": 0xffffffffffffffff, #Maximum value
|
||||
"step": 0.001, #Slider's step
|
||||
"display": "slider" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
"min_value":("FLOAT", {
|
||||
"default": 0,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.001,
|
||||
"display": "number"
|
||||
}),
|
||||
"max_value":("FLOAT", {
|
||||
"default": 1,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.001,
|
||||
"display": "number"
|
||||
}),
|
||||
"step":("FLOAT", {
|
||||
"default": 0.001,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.001,
|
||||
"display": "number"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -189,14 +271,18 @@ class FloatSlider:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,number):
|
||||
|
||||
return (number,)
|
||||
def run(self, number, min_value, max_value, step):
|
||||
if number < min_value:
|
||||
number = min_value
|
||||
elif number > max_value:
|
||||
number = max_value
|
||||
scaled_number = (number - min_value) / (max_value - min_value)
|
||||
return (scaled_number,)
|
||||
|
||||
|
||||
class IntNumber:
|
||||
@@ -207,9 +293,30 @@ class IntNumber:
|
||||
"default": 0,
|
||||
"min": -1, #Minimum value
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"min_value":("INT", {
|
||||
"default": 0,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"max_value":("INT", {
|
||||
"default": 1,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"step":("INT", {
|
||||
"default": 1,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step":1,
|
||||
"display": "number"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -217,13 +324,16 @@ class IntNumber:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,number):
|
||||
|
||||
def run(self,number,min_value,max_value,step):
|
||||
if number < min_value:
|
||||
number= min_value
|
||||
elif number > max_value:
|
||||
number= max_value
|
||||
return (number,)
|
||||
|
||||
class MultiplicationNode:
|
||||
@@ -245,7 +355,7 @@ class MultiplicationNode:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,False,)
|
||||
@@ -267,7 +377,7 @@ class TextInput:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
@@ -341,7 +451,7 @@ class DynamicDelayProcessor:
|
||||
RETURN_TYPES = (any_type,)
|
||||
RETURN_NAMES = ('output',)
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
def run(self,any_input,delay_seconds,delay_by_text,words_per_seconds,replace_output,replace_value):
|
||||
# print(f"Delay text:",delay_by_text )
|
||||
# 获取开始时间戳
|
||||
@@ -375,12 +485,12 @@ class AppInfo:
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"name": ("STRING",{"multiline": False,"default": "Mixlab-App","dynamicPrompts": False}),
|
||||
"image": ("IMAGE",),
|
||||
"input_ids":("STRING",{"multiline": True,"default": "\n".join(["1","2","3"]),"dynamicPrompts": False}),
|
||||
"output_ids":("STRING",{"multiline": True,"default": "\n".join(["5","9"]),"dynamicPrompts": False}),
|
||||
},
|
||||
|
||||
"optional":{
|
||||
"IMAGE": ("IMAGE",),
|
||||
"description":("STRING",{"multiline": True,"default": "","dynamicPrompts": False}),
|
||||
"version":("INT", {
|
||||
"default": 1,
|
||||
@@ -390,52 +500,49 @@ class AppInfo:
|
||||
"display": "number"
|
||||
}),
|
||||
"share_prefix":("STRING",{"multiline": False,"default": "","dynamicPrompts": False}),
|
||||
"link":("STRING",{"multiline": False,"default": "https://","dynamicPrompts": False}),
|
||||
"category":("STRING",{"multiline": False,"default": "","dynamicPrompts": False}),
|
||||
"auto_save": (["enable","disable"],),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
RETURN_TYPES = ()
|
||||
# RETURN_NAMES = ("IMAGE",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
# OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,name,image,input_ids,output_ids,description,version,share_prefix):
|
||||
def run(self,name,input_ids,output_ids,IMAGE,description,version,share_prefix,link,category,auto_save):
|
||||
name=name[0]
|
||||
|
||||
im=None
|
||||
if IMAGE:
|
||||
im=IMAGE[0][0]
|
||||
#TODO batch 的方式需要处理
|
||||
im=create_temp_file(im)
|
||||
# image [img,] img[batch,w,h,a] 列表里面是batch,
|
||||
|
||||
im=create_temp_file(image)
|
||||
input_ids=input_ids[0]
|
||||
output_ids=output_ids[0]
|
||||
description=description[0]
|
||||
version=version[0]
|
||||
share_prefix=share_prefix[0]
|
||||
link=link[0]
|
||||
category=category[0]
|
||||
|
||||
# id=get_json_hash([name,im,input_ids,output_ids,description,version])
|
||||
|
||||
return {"ui": {"json": [name,im,input_ids,output_ids,description,version,share_prefix]}, "result": (image,)}
|
||||
return {"ui": {"json": [name,im,input_ids,output_ids,description,version,share_prefix,link,category]}, "result": ()}
|
||||
|
||||
|
||||
|
||||
|
||||
class GetImageSize_:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("width", "height")
|
||||
|
||||
FUNCTION = "get_size"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
|
||||
def get_size(self, image):
|
||||
_, height, width, _ = image.shape
|
||||
return (width, height)
|
||||
|
||||
|
||||
|
||||
class SwitchByIndex:
|
||||
@classmethod
|
||||
@@ -451,6 +558,7 @@ class SwitchByIndex:
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"flat": (['off',"on"],),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,23 +567,32 @@ class SwitchByIndex:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self, A,B,index):
|
||||
def run(self, A,B,index,flat):
|
||||
|
||||
flat=flat[0]
|
||||
|
||||
C=[]
|
||||
index=index[0]
|
||||
for a in A:
|
||||
|
||||
for a in A:
|
||||
C.append(a)
|
||||
for b in B:
|
||||
C.append(b)
|
||||
|
||||
if flat=='on':
|
||||
C=flatten_list(C)
|
||||
|
||||
if index>-1:
|
||||
try:
|
||||
C=[C[index]]
|
||||
except Exception as e:
|
||||
C=[]
|
||||
|
||||
return (C,)
|
||||
|
||||
|
||||
@@ -508,7 +625,7 @@ class LimitNumber:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
@@ -530,3 +647,61 @@ class LimitNumber:
|
||||
|
||||
return (nn,)
|
||||
|
||||
|
||||
|
||||
class ListStatistics:
|
||||
@staticmethod
|
||||
def count_types(lst):
|
||||
type_count = {}
|
||||
|
||||
for item in lst:
|
||||
item_type = type(item).__name__
|
||||
if item_type not in type_count:
|
||||
type_count[item_type] = []
|
||||
|
||||
if item_type in ['dict', 'str', 'int', 'float']:
|
||||
type_count[item_type].append(item)
|
||||
|
||||
return type_count
|
||||
|
||||
# # 示例列表
|
||||
# my_list = [1, 'hello', {'name': 'John'}, 3.14, {'age': 25}, 'world', 10]
|
||||
|
||||
# # 创建ListStatistics对象
|
||||
# list_stats = ListStatistics()
|
||||
|
||||
# # 调用count_types方法进行统计
|
||||
# result = list_stats.count_types(my_list)
|
||||
|
||||
# # 输出结果
|
||||
# for item_type, values in result.items():
|
||||
# print(item_type + ':')
|
||||
# for value in values:
|
||||
# print(value)
|
||||
# print('---')
|
||||
|
||||
class TESTNODE_:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": { "ANY":(any_type,), },
|
||||
}
|
||||
|
||||
RETURN_TYPES = (any_type,)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/__TEST"
|
||||
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,ANY):
|
||||
print(ANY)
|
||||
# data=ANY
|
||||
list_stats = ListStatistics()
|
||||
|
||||
# 调用count_types方法进行统计
|
||||
result = list_stats.count_types(ANY)
|
||||
|
||||
return {"ui": {"data": result,"type":[str(type(ANY[0]))]}, "result": (ANY,)}
|
||||
|
||||
+2
-2
@@ -145,7 +145,7 @@ class VAELoader:
|
||||
RETURN_TYPES = ("VAE",)
|
||||
FUNCTION = "load_vae"
|
||||
|
||||
CATEGORY = "♾️Mixlab/_test"
|
||||
CATEGORY = "♾️Mixlab/__TEST"
|
||||
|
||||
#TODO: scale factor?
|
||||
def load_vae(self, vae_name):
|
||||
@@ -165,7 +165,7 @@ class VAEDecode:
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "decode"
|
||||
|
||||
CATEGORY = "♾️Mixlab/_test"
|
||||
CATEGORY = "♾️Mixlab/__TEST"
|
||||
|
||||
def decode(self, vae, samples):
|
||||
image = vae.decode(samples["samples"].to("cuda:0"))
|
||||
|
||||
+2
-1
@@ -4,4 +4,5 @@ watchdog
|
||||
opencv-python-headless
|
||||
matplotlib
|
||||
openai
|
||||
simple-lama-inpainting
|
||||
# simple-lama-inpainting
|
||||
# clip-interrogator==0.6.0
|
||||
+1088
-120
File diff suppressed because it is too large
Load Diff
@@ -196,6 +196,9 @@ app.registerExtension({
|
||||
}
|
||||
if (bg) {
|
||||
data.bg_image = await parseImage(bg)
|
||||
if (!data.bg_image.match('data:image/')) {
|
||||
delete data.bg_image
|
||||
}
|
||||
}
|
||||
|
||||
if (material) {
|
||||
|
||||
+152
-23
@@ -70,11 +70,13 @@ function extractInputAndOutputData (jsonData, inputIds = [], outputIds = []) {
|
||||
const data = jsonData
|
||||
const input = []
|
||||
const output = []
|
||||
const seed = {}
|
||||
|
||||
for (const id in data) {
|
||||
if (data.hasOwnProperty(id)) {
|
||||
let node = app.graph.getNodeById(id)
|
||||
if (inputIds.includes(id)) {
|
||||
let node = app.graph.getNodeById(id)
|
||||
// let node = app.graph.getNodeById(id)
|
||||
let options = []
|
||||
// 模型
|
||||
try {
|
||||
@@ -87,6 +89,30 @@ function extractInputAndOutputData (jsonData, inputIds = [], outputIds = []) {
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
if (node.type == 'IntNumber' || node.type == 'FloatSlider') {
|
||||
// min max step
|
||||
let [v, min, max, step] = Array.from(node.widgets, w => w.value)
|
||||
options = { min, max, step }
|
||||
// node.widgets.filter(w => w.type === 'number')[0].options
|
||||
}
|
||||
|
||||
if (node.type == 'PromptSlide') {
|
||||
// min max step
|
||||
options = node.widgets.filter(w => w.type === 'slider')[0].options
|
||||
// 备选的keywords清单
|
||||
try {
|
||||
let keywords = node.widgets.filter(w => w.name === 'upload')[0]
|
||||
.value
|
||||
keywords = JSON.parse(keywords)
|
||||
options.keywords = keywords
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type == 'Color') {
|
||||
}
|
||||
|
||||
input[inputIds.indexOf(id)] = {
|
||||
...data[id],
|
||||
title: node.title,
|
||||
@@ -96,14 +122,23 @@ function extractInputAndOutputData (jsonData, inputIds = [], outputIds = []) {
|
||||
// input.push()
|
||||
}
|
||||
if (outputIds.includes(id)) {
|
||||
let node = app.graph.getNodeById(id)
|
||||
// let node = app.graph.getNodeById(id)
|
||||
// output.push()
|
||||
output[outputIds.indexOf(id)] = { ...data[id], title: node.title, id }
|
||||
}
|
||||
|
||||
if (node.type === 'KSampler' || node.type == 'SamplerCustom') {
|
||||
// seed 的类型收集
|
||||
try {
|
||||
seed[id] = node.widgets.filter(
|
||||
w => w.name === 'seed' || w.name == 'noise_seed'
|
||||
)[0].linkedWidgets[0].value
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { input, output }
|
||||
return { input, output, seed }
|
||||
}
|
||||
|
||||
function getUrl () {
|
||||
@@ -113,6 +148,16 @@ function getUrl () {
|
||||
return url
|
||||
}
|
||||
|
||||
const getLocalData = key => {
|
||||
let data = {}
|
||||
try {
|
||||
data = JSON.parse(localStorage.getItem(key)) || {}
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
async function save_app (json) {
|
||||
let url = getUrl()
|
||||
|
||||
@@ -121,7 +166,8 @@ async function save_app (json) {
|
||||
body: JSON.stringify({
|
||||
data: json,
|
||||
task: 'save_app',
|
||||
filename: json.app.filename
|
||||
filename: json.app.filename,
|
||||
category: json.app.category
|
||||
})
|
||||
})
|
||||
return await res.json()
|
||||
@@ -143,10 +189,13 @@ function downloadJsonFile (jsonData, fileName = 'mix_app.json') {
|
||||
}, 0)
|
||||
}
|
||||
|
||||
async function save (json, download = false) {
|
||||
async function save (json, download = false, showInfo = true) {
|
||||
console.log('####SAVE', json[0])
|
||||
const name = json[0],
|
||||
version = json[5],
|
||||
share_prefix = json[6], //用于分享的功能扩展
|
||||
link = json[7], //用于创建界面上的跳转链接
|
||||
category = json[8] || '', //用于分类
|
||||
description = json[4],
|
||||
inputIds = json[2].split('\n').filter(f => f),
|
||||
outputIds = json[3].split('\n').filter(f => f)
|
||||
@@ -162,7 +211,7 @@ async function save (json, download = false) {
|
||||
try {
|
||||
let data = await app.graphToPrompt()
|
||||
|
||||
const { input, output } = extractInputAndOutputData(
|
||||
const { input, output, seed } = extractInputAndOutputData(
|
||||
data.output,
|
||||
inputIds,
|
||||
outputIds
|
||||
@@ -174,8 +223,11 @@ async function save (json, download = false) {
|
||||
version,
|
||||
input,
|
||||
output,
|
||||
seed, //控制是fixed 还是random
|
||||
share_prefix,
|
||||
filename: `${name}_${version}_${new Date().toDateString()}.json`
|
||||
link,
|
||||
category,
|
||||
filename: `${name}_${version}.json`
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -183,27 +235,54 @@ async function save (json, download = false) {
|
||||
} catch (error) {}
|
||||
// console.log(data.app)
|
||||
// let http_workflow = app.graph.serialize()
|
||||
|
||||
await save_app(data)
|
||||
if (download) {
|
||||
await save_app(data)
|
||||
await downloadJsonFile(data, data.app.filename)
|
||||
await downloadJsonFile(data, data.app.filename)
|
||||
}
|
||||
|
||||
if (showInfo) {
|
||||
let open = window.confirm(
|
||||
`You can now access the standalone application on a new page!\n${getUrl()}/mixlab/app?filename=${encodeURIComponent(data.app.filename)}`
|
||||
`You can now access the standalone application on a new page!\n${getUrl()}/mixlab/app?filename=${encodeURIComponent(
|
||||
data.app.filename
|
||||
)}&category=${encodeURIComponent(data.app.category)}`
|
||||
)
|
||||
if (open)
|
||||
window.open(
|
||||
`${getUrl()}/mixlab/app?filename=${encodeURIComponent(data.app.filename)}`
|
||||
`${getUrl()}/mixlab/app?filename=${encodeURIComponent(
|
||||
data.app.filename
|
||||
)}&category=${encodeURIComponent(data.app.category)}`
|
||||
)
|
||||
} else {
|
||||
await save_app(data)
|
||||
|
||||
let open = window.confirm(
|
||||
`You can now access the standalone application on a new page!\n${getUrl()}/mixlab/app`
|
||||
)
|
||||
if (open) window.open(`${getUrl()}/mixlab/app`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('###SpeechRecognition', error)
|
||||
console.log('###error', error)
|
||||
}
|
||||
}
|
||||
|
||||
function getInputsAndOutputs () {
|
||||
const inputs =
|
||||
`LoadImage CLIPTextEncode PromptSlide TextInput_ Color FloatSlider IntNumber CheckpointLoaderSimple LoraLoader`.split(
|
||||
' '
|
||||
),
|
||||
outputs = `PreviewImage SaveImage ShowTextForGPT VHS_VideoCombine`.split(
|
||||
' '
|
||||
)
|
||||
|
||||
let inputsId = [],
|
||||
outputsId = []
|
||||
|
||||
for (let node of app.graph._nodes) {
|
||||
if (inputs.includes(node.type)) {
|
||||
inputsId.push(node.id)
|
||||
}
|
||||
|
||||
if (outputs.includes(node.type)) {
|
||||
outputsId.push(node.id)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
input: inputsId,
|
||||
output: outputsId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +293,16 @@ app.registerExtension({
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
console.log('#orig_nodeCreated', this)
|
||||
// console.log('#orig_nodeCreated', this)
|
||||
|
||||
// 自动计算workflow里哪些节点支持
|
||||
let input_ids = this.widgets.filter(w => w.name == 'input_ids')[0],
|
||||
output_ids = this.widgets.filter(w => w.name == 'output_ids')[0]
|
||||
|
||||
const { input, output } = getInputsAndOutputs()
|
||||
input_ids.value = input.join('\n')
|
||||
output_ids.value = output.join('\n')
|
||||
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'AppInfoRun',
|
||||
@@ -285,15 +373,22 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
|
||||
window._mixlab_app_json = null
|
||||
}
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = async function (message) {
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
console.log(message.json)
|
||||
|
||||
window._mixlab_app_json = message.json
|
||||
try {
|
||||
let a = this.widgets.filter(w => w.name === 'AppInfoRun')[0]
|
||||
if (a) {
|
||||
if (!a.value) a.value = 0
|
||||
a.value += 1
|
||||
}
|
||||
|
||||
const div = this.widgets.filter(w => w.div)[0].div
|
||||
Array.from(
|
||||
div.querySelectorAll('button'),
|
||||
@@ -302,5 +397,39 @@ app.registerExtension({
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
console.log('#loadedGraphNode1111')
|
||||
window._mixlab_app_json = null //切换workflow需要清空
|
||||
if (node.type === 'AppInfo') {
|
||||
let auto_save = node.widgets.filter(w => w.name == 'auto_save')[0]
|
||||
if (auto_save) {
|
||||
if (!['enable', 'disable'].includes(auto_save.value)) {
|
||||
auto_save.value = 'enable'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
api.addEventListener('execution_start', async ({ detail }) => {
|
||||
console.log('#execution_start', detail)
|
||||
window._mixlab_app_json = null
|
||||
})
|
||||
|
||||
api.addEventListener('executed', async ({ detail }) => {
|
||||
console.log('#executed', detail)
|
||||
// window._mixlab_app_json=null;
|
||||
const { output } = getInputsAndOutputs()
|
||||
if (output.includes(parseInt(detail.node))) {
|
||||
let appinfo = app.graph.findNodesByType('AppInfo')[0]
|
||||
if (appinfo) {
|
||||
let auto_save = appinfo.widgets.filter(w => w.name == 'auto_save')[0]
|
||||
if (auto_save?.value === 'enable') {
|
||||
// 自动保存
|
||||
console.log('auto_save')
|
||||
if (window._mixlab_app_json) save(window._mixlab_app_json, false, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
|
||||
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
|
||||
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
|
||||
|
||||
const version = 'v0.8.0'
|
||||
const version = 'v0.12.0'
|
||||
|
||||
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
|
||||
.then(response => response.json())
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
// import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
function getRandomElements (arr, num) {
|
||||
var result = []
|
||||
var len = arr.length
|
||||
|
||||
for (var i = 0; i < num; i++) {
|
||||
var randomIndex = Math.floor(Math.random() * len)
|
||||
result.push(arr[randomIndex])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const createPrompt = (node, prompts, items, sample) => {
|
||||
const w = ComfyWidgets['STRING'](
|
||||
node,
|
||||
'text',
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
).widget
|
||||
w.inputEl.readOnly = true
|
||||
w.inputEl.style.opacity = 0.6
|
||||
|
||||
w.value = typeof prompts === 'string' ? prompts : prompts.join('\n\n')
|
||||
|
||||
const w2 = ComfyWidgets['STRING'](
|
||||
node,
|
||||
'text',
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
).widget
|
||||
w2.inputEl.readOnly = true
|
||||
w2.inputEl.style.opacity = 0.6
|
||||
|
||||
w2.value = typeof items === 'string' ? items : JSON.stringify(items, null, 2)
|
||||
|
||||
const w3 = ComfyWidgets['STRING'](
|
||||
node,
|
||||
'text',
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
).widget
|
||||
w3.inputEl.readOnly = true
|
||||
w3.inputEl.style.opacity = 0.6
|
||||
w3.value = typeof sample === 'string' ? sample : sample.join('\n\n')
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.prompt.ClipInterrogator',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeData.name === 'ClipInterrogator') {
|
||||
function populate (prompts, items, random_samples) {
|
||||
if (this.widgets) {
|
||||
for (let i = 0; i < this.widgets.length; i++) {
|
||||
if (this.widgets[i].type !== 'combo') this.widgets[i].onRemove?.()
|
||||
}
|
||||
this.widgets.length = 2
|
||||
}
|
||||
|
||||
createPrompt(this, prompts, items, random_samples)
|
||||
|
||||
// console.log('ClipInterrogator', w, w2)
|
||||
requestAnimationFrame(() => {
|
||||
const sz = this.computeSize()
|
||||
if (sz[0] < this.size[0]) {
|
||||
sz[0] = this.size[0]
|
||||
}
|
||||
if (sz[1] < this.size[1]) {
|
||||
sz[1] = this.size[1]
|
||||
}
|
||||
this.onResize?.(sz)
|
||||
app.graph.setDirtyCanvas(true, false)
|
||||
})
|
||||
}
|
||||
|
||||
// When the node is executed we will be sent the input text, display this in the widget
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
// console.log('##', message)
|
||||
populate.call(
|
||||
this,
|
||||
message.prompt,
|
||||
message.analysis,
|
||||
message.random_samples
|
||||
)
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
// Fires every time a node is constructed
|
||||
// You can modify widgets/add handlers/etc here
|
||||
|
||||
if (node.type === 'ClipInterrogator') {
|
||||
try {
|
||||
|
||||
let widgets_values = node.widgets_values
|
||||
console.log(widgets_values )
|
||||
try {
|
||||
if (widgets_values[2] && widgets_values[3] && widgets_values[4])
|
||||
createPrompt(
|
||||
node,
|
||||
widgets_values[2],
|
||||
widgets_values[3],
|
||||
widgets_values[4]
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -256,6 +256,7 @@ app.registerExtension({
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments);
|
||||
console.log('##',message.text)
|
||||
populate.call(this, message.text);
|
||||
};
|
||||
|
||||
|
||||
@@ -1800,15 +1800,19 @@ const updateUI = node => {
|
||||
pw.inputEl.title = `Total of ${prompts.length} prompts`
|
||||
} else {
|
||||
// 动态添加
|
||||
console.log('ComfyWidgets',ComfyWidgets.STRING(
|
||||
node,
|
||||
'prompts',
|
||||
['STRING', { multiline: true }]
|
||||
))
|
||||
// console.log('ComfyWidgets',ComfyWidgets.STRING(
|
||||
// node,
|
||||
// 'prompts',
|
||||
// ['STRING', { multiline: true }]
|
||||
// ))
|
||||
|
||||
// ComfyWidgets.STRING(this, "", ["", {default:this.properties.text, multiline: true}], app)
|
||||
|
||||
const w = ComfyWidgets.STRING(
|
||||
node,
|
||||
'prompts',
|
||||
['STRING', { multiline: true }]
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
).widget
|
||||
w.inputEl.readOnly = true
|
||||
w.inputEl.style.opacity = 0.6
|
||||
@@ -2089,13 +2093,13 @@ const node = {
|
||||
name: 'RandomPrompt',
|
||||
async init (app) {
|
||||
// Any initial setup to run as soon as the page loads
|
||||
console.log('[logging]', 'extension init')
|
||||
// console.log('[logging]', 'extension init')
|
||||
|
||||
if (window.location.href.match('/?')) {
|
||||
const { workflow } = getURLParameters(window.location.href)
|
||||
if (workflow)
|
||||
get_my_workflow().then(data => {
|
||||
console.log('#get_my_workflow', data)
|
||||
// console.log('#get_my_workflow', data)
|
||||
let my_workflow = data.filter(
|
||||
d => d.filename == 'my_workflow.json'
|
||||
)[0]
|
||||
@@ -2131,10 +2135,15 @@ const node = {
|
||||
// }
|
||||
},
|
||||
loadedGraphNode (node, app) {
|
||||
// Fires for each node when loading/dragging/etc a workflow json or png
|
||||
// If you break something in the backend and want to patch workflows in the frontend
|
||||
// This is the place to do this
|
||||
// console.log("[logging]", "loaded graph node: ", exportGraph(node.graph));
|
||||
if (node.type === 'RandomPrompt') {
|
||||
try {
|
||||
let max_count = node.widgets.filter(w => w.name === "max_count")[0];
|
||||
max_count.value= node.widgets_values[0]
|
||||
// console.log('RandomPrompt',max_count,node.widgets_values[0])
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
},
|
||||
async nodeCreated (node) {
|
||||
if (node.type === 'RandomPrompt') {
|
||||
@@ -2227,7 +2236,7 @@ const node = {
|
||||
const r = onExecuted?.apply?.(this, arguments)
|
||||
|
||||
let prompts = message.prompts
|
||||
console.log('executed', message)
|
||||
// console.log('executed', message)
|
||||
// console.log('#RandomPrompt', this.widgets)
|
||||
const pw = this.widgets.filter(w => w.name === 'prompts')[0]
|
||||
|
||||
@@ -2238,7 +2247,7 @@ const node = {
|
||||
} else {
|
||||
// 动态添加
|
||||
const w = ComfyWidgets.STRING(
|
||||
node,
|
||||
this,
|
||||
'prompts',
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
|
||||
@@ -0,0 +1,576 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
import PhotoSwipeLightbox from '/extensions/comfyui-mixlab-nodes/lib/photoswipe-lightbox.esm.min.js'
|
||||
function loadCSS (url) {
|
||||
var link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
link.type = 'text/css'
|
||||
link.href = url
|
||||
document.getElementsByTagName('head')[0].appendChild(link)
|
||||
|
||||
// Create a style element
|
||||
const style = document.createElement('style')
|
||||
// Define the CSS rule for scrollbar width
|
||||
const cssRule = `.pswp__custom-caption {
|
||||
background: rgb(20 27 70);
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
width: calc(100% - 32px);
|
||||
max-width: 980px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 16px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.pswp__custom-caption a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.hidden-caption-content {
|
||||
display: none;
|
||||
}`
|
||||
// Add the CSS rule to the style element
|
||||
style.appendChild(document.createTextNode(cssRule))
|
||||
|
||||
// Append the style element to the document head
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
loadCSS('/extensions/comfyui-mixlab-nodes/lib/photoswipe.min.css')
|
||||
|
||||
function initLightBox () {
|
||||
const lightbox = new PhotoSwipeLightbox({
|
||||
gallery: '.prompt_image_output',
|
||||
children: 'a',
|
||||
pswpModule: () =>
|
||||
import('/extensions/comfyui-mixlab-nodes/lib/photoswipe.esm.min.js')
|
||||
})
|
||||
|
||||
lightbox.on('uiRegister', function () {
|
||||
lightbox.pswp.ui.registerElement({
|
||||
name: 'custom-caption',
|
||||
order: 9,
|
||||
isButton: false,
|
||||
appendTo: 'root',
|
||||
html: 'Caption text',
|
||||
onInit: (el, pswp) => {
|
||||
lightbox.pswp.on('change', () => {
|
||||
const currSlideElement = lightbox.pswp.currSlide.data.element
|
||||
let captionHTML = ''
|
||||
if (currSlideElement) {
|
||||
const hiddenCaption = currSlideElement.querySelector(
|
||||
'.hidden-caption-content'
|
||||
)
|
||||
if (hiddenCaption) {
|
||||
// get caption from element with class hidden-caption-content
|
||||
captionHTML = hiddenCaption.innerHTML
|
||||
} else {
|
||||
// get caption from alt attribute
|
||||
captionHTML = currSlideElement
|
||||
.querySelector('img')
|
||||
.getAttribute('alt')
|
||||
}
|
||||
}
|
||||
el.innerHTML = captionHTML || ''
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
lightbox.init()
|
||||
}
|
||||
|
||||
function get_position_style (ctx, widget_width, y, node_height) {
|
||||
const MARGIN = 4 // the margin around the html element
|
||||
|
||||
/* Create a transform that deals with all the scrolling and zooming */
|
||||
const elRect = ctx.canvas.getBoundingClientRect()
|
||||
const transform = new DOMMatrix()
|
||||
.scaleSelf(
|
||||
elRect.width / ctx.canvas.width,
|
||||
elRect.height / ctx.canvas.height
|
||||
)
|
||||
.multiplySelf(ctx.getTransform())
|
||||
.translateSelf(MARGIN, MARGIN + y)
|
||||
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||
// maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||
width: `${widget_width - MARGIN * 2 - 24}px`,
|
||||
// height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||
// background: '#EEEEEE',
|
||||
paddingLeft: '12px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
// alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
}
|
||||
}
|
||||
function createImage (url) {
|
||||
let im = new Image()
|
||||
return new Promise((res, rej) => {
|
||||
im.onload = () => res(im)
|
||||
im.src = url
|
||||
})
|
||||
}
|
||||
|
||||
async function fetchImage (url) {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const blob = await response.blob()
|
||||
|
||||
return blob
|
||||
} catch (error) {
|
||||
console.error('出现错误:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const getLocalData = key => {
|
||||
let data = {}
|
||||
try {
|
||||
data = JSON.parse(localStorage.getItem(key)) || {}
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
const setLocalDataOfWin = (key, value) => {
|
||||
localStorage.setItem(key, JSON.stringify(value))
|
||||
// window[key] = value
|
||||
}
|
||||
|
||||
const createSelect = (select, opts, targetWidget) => {
|
||||
select.style.display = 'block'
|
||||
let html = ''
|
||||
let isMatch = false
|
||||
for (const opt of opts) {
|
||||
html += `<option value='${opt}' ${
|
||||
targetWidget.value === opt ? 'selected' : ''
|
||||
}>${opt}</option>`
|
||||
if (targetWidget.value === opt) isMatch = true
|
||||
}
|
||||
select.innerHTML = html
|
||||
if (!isMatch) targetWidget.value = opts[0]
|
||||
// 添加change事件监听器
|
||||
select.addEventListener('change', function () {
|
||||
// 获取选中的选项的值
|
||||
var selectedOption = select.options[select.selectedIndex].value
|
||||
targetWidget.value = selectedOption
|
||||
// console.log(widget,selectedOption)
|
||||
})
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.prompt.RandomPrompt',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass == 'RandomPrompt') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = async function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
name
|
||||
|
||||
const mutable_prompt = this.widgets.filter(
|
||||
w => w.name == 'mutable_prompt'
|
||||
)[0]
|
||||
// console.log('PromptSlide nodeData', prompt_keyword)
|
||||
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'upload',
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, y, node.size[1])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
widget.div = $el('div', {})
|
||||
|
||||
const btn = document.createElement('button')
|
||||
btn.innerText = 'Upload Keywords'
|
||||
|
||||
btn.style = `cursor: pointer;
|
||||
font-weight: 300;
|
||||
margin: 2px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid; height: 30px;min-width: 122px;
|
||||
`
|
||||
|
||||
// const btn=document.createElement('button');
|
||||
// btn.innerText='Upload'
|
||||
btn.addEventListener('click', () => {
|
||||
let inp = document.createElement('input')
|
||||
inp.type = 'file'
|
||||
inp.accept = '.txt'
|
||||
inp.click()
|
||||
inp.addEventListener('change', event => {
|
||||
// 获取选择的文件
|
||||
const file = event.target.files[0]
|
||||
this.title = file.name.split('.')[0]
|
||||
|
||||
// console.log(file.name.split('.')[0])
|
||||
// 创建文件读取器
|
||||
const reader = new FileReader()
|
||||
|
||||
// 定义读取完成事件的回调函数
|
||||
reader.onload = event => {
|
||||
// 读取完成后的文本内容
|
||||
const fileContent = event.target.result.split('\n')
|
||||
const keywords = Array.from(fileContent, f => f.trim()).filter(
|
||||
f => f
|
||||
)
|
||||
// 打印文件内容
|
||||
// console.log(keywords)
|
||||
|
||||
mutable_prompt.value = keywords.join('\n')
|
||||
|
||||
inp.remove()
|
||||
}
|
||||
|
||||
// 以文本方式读取文件
|
||||
reader.readAsText(file)
|
||||
})
|
||||
})
|
||||
|
||||
widget.div.appendChild(btn)
|
||||
document.body.appendChild(widget.div)
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
const onRemoved = this.onRemoved
|
||||
this.onRemoved = () => {
|
||||
widget.div.remove()
|
||||
return onRemoved?.()
|
||||
}
|
||||
|
||||
if (this.onResize) {
|
||||
this.onResize(this.size)
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'RandomPrompt') {
|
||||
// try {
|
||||
// let mutable_prompt = node.widgets.filter(w => w.name === 'mutable_prompt')[0]
|
||||
// // let ks = getLocalData(`_mixlab_PromptSlide`)
|
||||
// let uploadWidget = node.widgets.filter(w => w.name == 'upload')[0]
|
||||
// // console.log('##widget', uploadWidget.value)
|
||||
// let keywords = JSON.parse(uploadWidget.value)
|
||||
// if (keywords && keywords[0]) {
|
||||
// mutable_prompt.value=keywords.join('\n')
|
||||
// }
|
||||
// } catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.prompt.PromptSlide',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass == 'PromptSlide') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = async function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
const prompt_keyword = this.widgets.filter(
|
||||
w => w.name == 'prompt_keyword'
|
||||
)[0]
|
||||
// console.log('PromptSlide nodeData', prompt_keyword)
|
||||
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'upload',
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, y, node.size[1])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
widget.div = $el('div', {})
|
||||
|
||||
const btn = document.createElement('button')
|
||||
btn.innerText = 'Upload Keywords'
|
||||
|
||||
btn.style = `cursor: pointer;
|
||||
font-weight: 300;
|
||||
margin: 2px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid; height: 30px;min-width: 122px;
|
||||
`
|
||||
|
||||
const select = document.createElement('select')
|
||||
select.style = `display:none;cursor: pointer;
|
||||
font-weight: 300;
|
||||
margin: 2px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid; height: 30px;min-width: 100px;
|
||||
`
|
||||
widget.select = select
|
||||
|
||||
// const btn=document.createElement('button');
|
||||
// btn.innerText='Upload'
|
||||
btn.addEventListener('click', () => {
|
||||
let inp = document.createElement('input')
|
||||
inp.type = 'file'
|
||||
inp.accept = '.txt'
|
||||
inp.click()
|
||||
inp.addEventListener('change', event => {
|
||||
// 获取选择的文件
|
||||
const file = event.target.files[0]
|
||||
this.title = file.name.split('.')[0]
|
||||
|
||||
// console.log(file.name.split('.')[0])
|
||||
// 创建文件读取器
|
||||
const reader = new FileReader()
|
||||
|
||||
// 定义读取完成事件的回调函数
|
||||
reader.onload = event => {
|
||||
// 读取完成后的文本内容
|
||||
const fileContent = event.target.result.split('\n')
|
||||
const keywords = Array.from(fileContent, f => f.trim()).filter(
|
||||
f => f
|
||||
)
|
||||
// 打印文件内容
|
||||
// console.log(keywords)
|
||||
|
||||
widget.value = JSON.stringify(keywords)
|
||||
|
||||
// let ks = getLocalData(`_mixlab_PromptSlide`)
|
||||
// ks[this.id] = keywords
|
||||
// setLocalDataOfWin(`_mixlab_PromptSlide`, ks)
|
||||
|
||||
createSelect(select, keywords, prompt_keyword)
|
||||
|
||||
inp.remove()
|
||||
}
|
||||
|
||||
// 以文本方式读取文件
|
||||
reader.readAsText(file)
|
||||
})
|
||||
})
|
||||
|
||||
widget.div.appendChild(btn)
|
||||
widget.div.appendChild(select)
|
||||
document.body.appendChild(widget.div)
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
const onRemoved = this.onRemoved
|
||||
this.onRemoved = () => {
|
||||
widget.div.remove()
|
||||
return onRemoved?.()
|
||||
}
|
||||
|
||||
if (this.onResize) {
|
||||
this.onResize(this.size)
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'PromptSlide') {
|
||||
try {
|
||||
let prompt = node.widgets.filter(w => w.name === 'prompt_keyword')[0]
|
||||
// let ks = getLocalData(`_mixlab_PromptSlide`)
|
||||
let uploadWidget = node.widgets.filter(w => w.name == 'upload')[0]
|
||||
// console.log('##widget', uploadWidget.value)
|
||||
let keywords = JSON.parse(uploadWidget.value)
|
||||
// console.log('keywords',keywords)
|
||||
let widget = node.widgets.filter(w => w.select)[0]
|
||||
if (keywords && keywords[0]) {
|
||||
widget.select.style.display = 'block'
|
||||
createSelect(widget.select, keywords, prompt)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const _createResult = async (node, widget, message) => {
|
||||
widget.div.innerHTML = ``
|
||||
|
||||
const width = node.size[0] * 0.5 - 12
|
||||
|
||||
let height_add = 0
|
||||
|
||||
for (let index = 0; index < message._images.length; index++) {
|
||||
const imgs = message._images[index]
|
||||
|
||||
for (const img of imgs) {
|
||||
let url = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(img.filename)}&type=${
|
||||
img.type
|
||||
}&subfolder=${
|
||||
img.subfolder
|
||||
}${app.getPreviewFormatParam()}${app.getRandParam()}`
|
||||
)
|
||||
|
||||
let image = await createImage(url)
|
||||
|
||||
// 创建card
|
||||
let div = document.createElement('div')
|
||||
div.className = 'card'
|
||||
div.draggable = true
|
||||
|
||||
div.ondragend = async event => {
|
||||
console.log('拖动停止')
|
||||
let url = div.querySelector('img').src
|
||||
|
||||
let blob = await fetchImage(url)
|
||||
|
||||
let imageNode = null
|
||||
// No image node selected: add a new one
|
||||
if (!imageNode) {
|
||||
const newNode = LiteGraph.createNode('LoadImage')
|
||||
newNode.pos = [...app.canvas.graph_mouse]
|
||||
imageNode = app.graph.add(newNode)
|
||||
app.graph.change()
|
||||
}
|
||||
|
||||
// const blob = item.getAsFile();
|
||||
imageNode.pasteFile(blob)
|
||||
}
|
||||
|
||||
div.setAttribute('data-scale', image.naturalHeight / image.naturalWidth)
|
||||
|
||||
let h = (image.naturalHeight * width) / image.naturalWidth
|
||||
if (index % 2 === 0) height_add += h
|
||||
div.style = `width: ${width}px;height:${h}px;position: relative;margin: 4px;`
|
||||
|
||||
div.innerHTML = `<a href="${url}"
|
||||
data-pswp-width="${image.naturalWidth}"
|
||||
data-pswp-height="${image.naturalHeight}"
|
||||
target="_blank">
|
||||
<img src="${url}" style='width: 100%' alt="${message.prompts[index]}"/>
|
||||
</a>
|
||||
<p style="position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0.6;
|
||||
background-color: var(--comfy-input-bg);
|
||||
color: var(--descrip-text);
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
padding: 5px;
|
||||
text-align: left;">${message.prompts[index]}</p>`
|
||||
widget.div.appendChild(div)
|
||||
}
|
||||
}
|
||||
|
||||
node.size[1] = 98 + height_add
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.prompt.PromptImage',
|
||||
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass == 'PromptImage') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
console.log('#orig_nodeCreated', this)
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'result',
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(this.div.style, {
|
||||
...get_position_style(ctx, widget_width, y, node.size[1]),
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
// outline: '1px solid red',
|
||||
paddingLeft: '0px',
|
||||
width: widget_width + 'px'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
widget.div = $el('div', {})
|
||||
widget.div.className = 'prompt_image_output'
|
||||
|
||||
document.body.appendChild(widget.div)
|
||||
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
initLightBox()
|
||||
|
||||
const onRemoved = this.onRemoved
|
||||
this.onRemoved = () => {
|
||||
widget.div.remove()
|
||||
return onRemoved?.()
|
||||
}
|
||||
|
||||
const onResize = this.onResize
|
||||
this.onResize = function () {
|
||||
// 缩放发生
|
||||
// console.log('##缩放发生', this.size)
|
||||
let w = this.size[0] * 0.5 - 12
|
||||
Array.from(widget.div.querySelectorAll('.card'), card => {
|
||||
card.style.width = `${w}px`
|
||||
card.style.height = `${
|
||||
w * parseFloat(card.getAttribute('data-scale'))
|
||||
}px`
|
||||
})
|
||||
return onResize?.apply(this, arguments)
|
||||
}
|
||||
|
||||
// this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = async function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
console.log('#PromptImage', message.prompts, message._images)
|
||||
// window._mixlab_app_json = message.json
|
||||
try {
|
||||
let widget = this.widgets.filter(w => w.name === 'result')[0]
|
||||
widget.value = message
|
||||
_createResult(this, widget, { ...message })
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'PromptImage') {
|
||||
// await sleep(0)
|
||||
let widget = node.widgets.filter(w => w.name === 'result')[0]
|
||||
console.log('widget.value', widget.value)
|
||||
|
||||
initLightBox()
|
||||
|
||||
let cards = widget.div.querySelectorAll('.card')
|
||||
if (cards.length == 0) node.size = [280, 120]
|
||||
|
||||
_createResult(node, widget, widget.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
+371
-172
@@ -4,7 +4,6 @@ import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
import { closeIcon } from './svg_icons.js'
|
||||
|
||||
|
||||
import {
|
||||
GroupNodeConfig,
|
||||
GroupNodeHandler
|
||||
@@ -49,6 +48,40 @@ async function get_nodes_map () {
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
function get_url () {
|
||||
let api_host = `${window.location.hostname}:${window.location.port}`
|
||||
let api_base = ''
|
||||
let url = `${window.location.protocol}//${api_host}${api_base}`
|
||||
return url
|
||||
}
|
||||
|
||||
async function get_my_app (filename = null, category = '') {
|
||||
let url = get_url()
|
||||
const res = await fetch(`${url}/mixlab/workflow`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
task: 'my_app',
|
||||
filename,
|
||||
category,
|
||||
admin: true
|
||||
})
|
||||
})
|
||||
let result = await res.json()
|
||||
let data = []
|
||||
try {
|
||||
for (const res of result.data) {
|
||||
let { app, workflow } = res.data
|
||||
if (app.filename)
|
||||
data.push({
|
||||
...app,
|
||||
data: workflow,
|
||||
date: res.date
|
||||
})
|
||||
}
|
||||
} catch (error) {}
|
||||
return data
|
||||
}
|
||||
|
||||
function loadCSS (url) {
|
||||
var link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
@@ -668,191 +701,357 @@ app.registerExtension({
|
||||
...options
|
||||
] // and return the options
|
||||
}
|
||||
LGraphCanvas.prototype.centerOnNode = function(node) {
|
||||
var dpr = window.devicePixelRatio || 1; // 获取设备像素比
|
||||
LGraphCanvas.prototype.centerOnNode = function (node) {
|
||||
// console.log(node)
|
||||
var dpr = window.devicePixelRatio || 1 // 获取设备像素比
|
||||
this.ds.offset[0] =
|
||||
-node.pos[0] -
|
||||
node.size[0] * 0.5 +
|
||||
(this.canvas.width * 0.5) / (this.ds.scale * dpr); // 考虑设备像素比
|
||||
-node.pos[0] -
|
||||
node.size[0] * 0.5 +
|
||||
(this.canvas.width * 0.5) / (this.ds.scale * dpr) // 考虑设备像素比
|
||||
this.ds.offset[1] =
|
||||
-node.pos[1] -
|
||||
node.size[1] * 0.5 +
|
||||
(this.canvas.height * 0.5) / (this.ds.scale * dpr); // 考虑设备像素比
|
||||
this.setDirty(true, true);
|
||||
};
|
||||
-node.pos[1] -
|
||||
node.size[1] * 0.5 +
|
||||
(this.canvas.height * 0.5) / (this.ds.scale * dpr) // 考虑设备像素比
|
||||
this.setDirty(true, true)
|
||||
}
|
||||
},
|
||||
async setup () {
|
||||
// Add canvas menu options
|
||||
const orig = LGraphCanvas.prototype.getCanvasMenuOptions
|
||||
setup () {
|
||||
setTimeout(async () => {
|
||||
// Add canvas menu options
|
||||
const orig = LGraphCanvas.prototype.getCanvasMenuOptions
|
||||
|
||||
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
|
||||
const options = orig.apply(this, arguments)
|
||||
const apps = await get_my_app()
|
||||
|
||||
options.push(null, {
|
||||
content: `Nodes Map ♾️Mixlab`,
|
||||
disabled: false, // or a function determining whether to disable
|
||||
callback: async () => {
|
||||
nodesMap =
|
||||
nodesMap && Object.keys(nodesMap).length > 0
|
||||
? nodesMap
|
||||
: await getCustomnodeMappings('url')
|
||||
let apps_map = { '0': [] }
|
||||
|
||||
const nodesDiv = document.createDocumentFragment()
|
||||
const nodes = (await app.graphToPrompt()).output
|
||||
for (const app of apps) {
|
||||
if (app.category) {
|
||||
if (!apps_map[app.category]) apps_map[app.category] = []
|
||||
apps_map[app.category].push(app)
|
||||
} else {
|
||||
apps_map['0'].push(app)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log('[Mixlab]', 'loaded graph node: ', app)
|
||||
let div =
|
||||
document.querySelector('#mixlab_find_the_node') ||
|
||||
document.createElement('div')
|
||||
div.id = 'mixlab_find_the_node'
|
||||
div.style = `
|
||||
flex-direction: column;
|
||||
align-items: end;
|
||||
display:flex;position: absolute;
|
||||
top: 50px; left: 50px; width: 200px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
padding: 10px;
|
||||
border: 1px solid black;z-index: 999999999;padding-top: 0;`
|
||||
|
||||
div.innerHTML = ''
|
||||
|
||||
let btn = document.createElement('div')
|
||||
btn.style = `display: flex;
|
||||
width: calc(100% - 24px);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
height: 44px;`
|
||||
let btnB = document.createElement('button')
|
||||
let textB = document.createElement('p')
|
||||
btn.appendChild(textB)
|
||||
btn.appendChild(btnB)
|
||||
textB.style.fontSize = '12px'
|
||||
textB.innerText = `Locate and navigate nodes ♾️Mixlab`
|
||||
|
||||
btnB.style = `float: right; border: none; color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg); border-color: var(--border-color);cursor: pointer;`
|
||||
btnB.addEventListener('click', () => {
|
||||
div.style.display = 'none'
|
||||
})
|
||||
btnB.innerText = 'X'
|
||||
|
||||
// 悬浮框拖动事件
|
||||
div.addEventListener('mousedown', function (e) {
|
||||
var startX = e.clientX
|
||||
var startY = e.clientY
|
||||
var offsetX = div.offsetLeft
|
||||
var offsetY = div.offsetTop
|
||||
|
||||
function moveBox (e) {
|
||||
var newX = e.clientX
|
||||
var newY = e.clientY
|
||||
var deltaX = newX - startX
|
||||
var deltaY = newY - startY
|
||||
div.style.left = offsetX + deltaX + 'px'
|
||||
div.style.top = offsetY + deltaY + 'px'
|
||||
}
|
||||
|
||||
function stopMoving () {
|
||||
document.removeEventListener('mousemove', moveBox)
|
||||
document.removeEventListener('mouseup', stopMoving)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', moveBox)
|
||||
document.addEventListener('mouseup', stopMoving)
|
||||
})
|
||||
|
||||
div.appendChild(btn)
|
||||
|
||||
const updateNodes = (ns, nd) => {
|
||||
for (let nodeId in ns) {
|
||||
let n = ns[nodeId].class_type
|
||||
if (nodesMap[n]) {
|
||||
const { url, title } = nodesMap[n]
|
||||
let d = document.createElement('button')
|
||||
d.style = `text-align: left;margin:6px;color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg); border-color: var(--border-color);cursor: pointer;`
|
||||
d.addEventListener('click', () => {
|
||||
const node = app.graph.getNodeById(nodeId)
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(1)
|
||||
})
|
||||
d.addEventListener('mouseover', async () => {
|
||||
// console.log('mouseover')
|
||||
let n = (await app.graphToPrompt()).output
|
||||
if (!deepEqual(n, ns)) {
|
||||
nd.innerHTML = ''
|
||||
updateNodes(n, nd)
|
||||
}
|
||||
})
|
||||
|
||||
d.innerHTML = `
|
||||
<span>${'#' + nodeId} ${n}</span>
|
||||
<a href="${url}" target="_blank" style="text-decoration: none;">🔗</a>
|
||||
`
|
||||
d.title = title
|
||||
|
||||
nd.appendChild(d)
|
||||
let apps_opts = []
|
||||
for (const category in apps_map) {
|
||||
console.log('category',typeof(category))
|
||||
if (category === '0') {
|
||||
apps_opts.push(
|
||||
...Array.from(apps_map[category], a => {
|
||||
return {
|
||||
content: a.name,
|
||||
has_submenu: false,
|
||||
callback: async () => {
|
||||
try {
|
||||
let item = (await get_my_app(a.filename))[0]
|
||||
if (item) {
|
||||
// console.log(item.data)
|
||||
app.loadGraphData(item.data)
|
||||
setTimeout(() => {
|
||||
const node = app.graph._nodes_in_order[0]
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(0.5)
|
||||
}, 1000)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let nodesDivv = document.createElement('div')
|
||||
|
||||
for (let nodeId in nodes) {
|
||||
let n = nodes[nodeId].class_type
|
||||
if (nodesMap[n]) {
|
||||
const { url, title: _title } = nodesMap[n]
|
||||
let title = app.graph.getNodeById(nodeId).title || _title
|
||||
let d = document.createElement('button')
|
||||
d.style = `text-align: left;margin:6px;color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg); border-color: var(--border-color);cursor: pointer;`
|
||||
d.addEventListener('click', () => {
|
||||
const node = app.graph.getNodeById(nodeId)
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(1)
|
||||
})
|
||||
d.addEventListener('mouseover', async () => {
|
||||
console.log('mouseover')
|
||||
let n = (await app.graphToPrompt()).output
|
||||
if (!deepEqual(n, nodes)) {
|
||||
nodesDivv.innerHTML = ''
|
||||
updateNodes(n, nodesDivv)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
// 二级
|
||||
apps_opts.push({
|
||||
content: '🚀 '+category,
|
||||
has_submenu: true,
|
||||
disabled: false,
|
||||
submenu: {
|
||||
options: Array.from(apps_map[category], a => {
|
||||
return {
|
||||
content: a.name,
|
||||
callback: async () => {
|
||||
try {
|
||||
let item = (await get_my_app(a.filename, a.category))[0]
|
||||
if (item) {
|
||||
// console.log(item.data)
|
||||
app.loadGraphData(item.data)
|
||||
setTimeout(() => {
|
||||
const node = app.graph._nodes_in_order[0]
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(0.5)
|
||||
}, 1000)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
d.innerHTML = `
|
||||
<span>${'#' + nodeId} ${title}</span>
|
||||
<a href="${url}" target="_blank" style="text-decoration: none;">🔗</a>
|
||||
`
|
||||
d.title = n
|
||||
// console.log('apps',apps_map, apps_opts,apps)
|
||||
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
|
||||
const options = orig.apply(this, arguments)
|
||||
|
||||
nodesDiv.appendChild(d)
|
||||
options.push(
|
||||
null,
|
||||
{
|
||||
content: `Nodes Map ♾️Mixlab`,
|
||||
disabled: false,
|
||||
callback: async () => {
|
||||
nodesMap =
|
||||
nodesMap && Object.keys(nodesMap).length > 0
|
||||
? nodesMap
|
||||
: await getCustomnodeMappings('url')
|
||||
|
||||
const nodesDiv = document.createDocumentFragment()
|
||||
const nodes = (await app.graphToPrompt()).output
|
||||
|
||||
// console.log('[Mixlab]', 'loaded graph node: ', app)
|
||||
let div =
|
||||
document.querySelector('#mixlab_find_the_node') ||
|
||||
document.createElement('div')
|
||||
div.id = 'mixlab_find_the_node'
|
||||
div.style = `
|
||||
flex-direction: column;
|
||||
align-items: end;
|
||||
display:flex;position: absolute;
|
||||
top: 50px; left: 50px; width: 200px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
padding: 10px;
|
||||
border: 1px solid black;z-index: 999999999;padding-top: 0;`
|
||||
|
||||
div.innerHTML = ''
|
||||
|
||||
let btn = document.createElement('div')
|
||||
btn.style = `display: flex;
|
||||
width: calc(100% - 24px);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
height: 44px;`
|
||||
let btnB = document.createElement('button')
|
||||
let textB = document.createElement('p')
|
||||
btn.appendChild(textB)
|
||||
btn.appendChild(btnB)
|
||||
textB.style.fontSize = '12px'
|
||||
textB.innerText = `Locate and navigate nodes ♾️Mixlab`
|
||||
|
||||
btnB.style = `float: right; border: none; color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg); border-color: var(--border-color);cursor: pointer;`
|
||||
btnB.addEventListener('click', () => {
|
||||
div.style.display = 'none'
|
||||
})
|
||||
btnB.innerText = 'X'
|
||||
|
||||
// 悬浮框拖动事件
|
||||
div.addEventListener('mousedown', function (e) {
|
||||
var startX = e.clientX
|
||||
var startY = e.clientY
|
||||
var offsetX = div.offsetLeft
|
||||
var offsetY = div.offsetTop
|
||||
|
||||
function moveBox (e) {
|
||||
var newX = e.clientX
|
||||
var newY = e.clientY
|
||||
var deltaX = newX - startX
|
||||
var deltaY = newY - startY
|
||||
div.style.left = offsetX + deltaX + 'px'
|
||||
div.style.top = offsetY + deltaY + 'px'
|
||||
}
|
||||
|
||||
function stopMoving () {
|
||||
document.removeEventListener('mousemove', moveBox)
|
||||
document.removeEventListener('mouseup', stopMoving)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', moveBox)
|
||||
document.addEventListener('mouseup', stopMoving)
|
||||
})
|
||||
|
||||
div.appendChild(btn)
|
||||
|
||||
const updateNodes = (ns, nd) => {
|
||||
let appInfoNodes = {}
|
||||
try {
|
||||
let appInfo = app.graph._nodes.filter(
|
||||
n => n.type === 'AppInfo'
|
||||
)[0]
|
||||
if (appInfo) {
|
||||
appInfoNodes[appInfo.id] = 2
|
||||
for (const id of appInfo.widgets[1].value.split('\n')) {
|
||||
if (id && id.trim() && parseInt(id)) {
|
||||
appInfoNodes[id] = 0
|
||||
}
|
||||
}
|
||||
for (const id of app.graph._nodes
|
||||
.filter(n => n.type === 'AppInfo')[0]
|
||||
.widgets[2].value.split('\n')) {
|
||||
if (id && id.trim() && parseInt(id)) {
|
||||
appInfoNodes[id] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
for (let nodeId in ns) {
|
||||
let n = ns[nodeId].title || ns[nodeId].class_type
|
||||
if (nodesMap[n]) {
|
||||
const { url, title } = nodesMap[n]
|
||||
let d = document.createElement('button')
|
||||
d.style = `text-align: left;
|
||||
margin:6px;
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-color: ${
|
||||
appInfoNodes[nodeId] >= 0
|
||||
? appInfoNodes[nodeId] === 1
|
||||
? 'blue'
|
||||
: 'red'
|
||||
: 'var(--border-color)'
|
||||
};
|
||||
cursor: pointer;`
|
||||
|
||||
if (appInfoNodes[nodeId] === 2) {
|
||||
// appinfo
|
||||
d.style.backgroundColor = '#326328'
|
||||
d.style.color = '#ffffff'
|
||||
d.style.borderColor = 'transparent'
|
||||
}
|
||||
|
||||
d.addEventListener('click', () => {
|
||||
// console.log('node')
|
||||
const node = app.graph.getNodeById(nodeId)
|
||||
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(1)
|
||||
})
|
||||
d.addEventListener('mouseover', async () => {
|
||||
// console.log('mouseover')
|
||||
let n = (await app.graphToPrompt()).output
|
||||
if (!deepEqual(n, ns)) {
|
||||
nd.innerHTML = ''
|
||||
updateNodes(n, nd)
|
||||
}
|
||||
})
|
||||
|
||||
d.innerHTML = `
|
||||
<span>${'#' + nodeId} ${n}</span>
|
||||
<a href="${url}" target="_blank" style="text-decoration: none;">🔗</a>
|
||||
`
|
||||
d.title = title
|
||||
|
||||
nd.appendChild(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let nodesDivv = document.createElement('div')
|
||||
let appInfoNodes = {}
|
||||
try {
|
||||
let appInfo = app.graph._nodes.filter(
|
||||
n => n.type === 'AppInfo'
|
||||
)[0]
|
||||
if (appInfo) {
|
||||
appInfoNodes[appInfo.id] = 2
|
||||
for (const id of appInfo.widgets[1].value.split('\n')) {
|
||||
if (id && id.trim() && parseInt(id)) {
|
||||
appInfoNodes[id] = 0
|
||||
}
|
||||
}
|
||||
for (const id of app.graph._nodes
|
||||
.filter(n => n.type === 'AppInfo')[0]
|
||||
.widgets[2].value.split('\n')) {
|
||||
if (id && id.trim() && parseInt(id)) {
|
||||
appInfoNodes[id] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
for (let nodeId in nodes) {
|
||||
let n = nodes[nodeId].class_type
|
||||
if (nodesMap[n]) {
|
||||
const { url, title: _title } = nodesMap[n]
|
||||
let title = app.graph.getNodeById(nodeId).title || _title
|
||||
let d = document.createElement('button')
|
||||
d.style = `text-align: left;
|
||||
margin:6px;
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-color: ${
|
||||
appInfoNodes[nodeId] >= 0
|
||||
? appInfoNodes[nodeId] === 1
|
||||
? 'blue'
|
||||
: 'red'
|
||||
: 'var(--border-color)'
|
||||
};
|
||||
cursor: pointer;`
|
||||
|
||||
if (appInfoNodes[nodeId] === 2) {
|
||||
// appinfo
|
||||
d.style.backgroundColor = '#326328'
|
||||
d.style.color = '#ffffff'
|
||||
d.style.borderColor = 'transparent'
|
||||
}
|
||||
|
||||
d.addEventListener('click', () => {
|
||||
console.log('click')
|
||||
const node = app.graph.getNodeById(nodeId)
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(1)
|
||||
})
|
||||
d.addEventListener('mouseover', async () => {
|
||||
// console.log('mouseover')
|
||||
let n = (await app.graphToPrompt()).output
|
||||
if (!deepEqual(n, nodes)) {
|
||||
nodesDivv.innerHTML = ''
|
||||
updateNodes(n, nodesDivv)
|
||||
}
|
||||
})
|
||||
|
||||
d.innerHTML = `
|
||||
<span>${'#' + nodeId} ${title}</span>
|
||||
<a href="${url}" target="_blank" style="text-decoration: none;">🔗</a>
|
||||
`
|
||||
d.title = n
|
||||
|
||||
nodesDiv.appendChild(d)
|
||||
}
|
||||
}
|
||||
|
||||
nodesDivv.appendChild(nodesDiv)
|
||||
nodesDivv.style = `overflow: scroll;
|
||||
height: 70vh;width: 100%;`
|
||||
|
||||
div.appendChild(nodesDivv)
|
||||
|
||||
if (!document.querySelector('#mixlab_find_the_node'))
|
||||
document.body.appendChild(div)
|
||||
}
|
||||
},
|
||||
{
|
||||
content: 'Workflow App ♾️Mixlab',
|
||||
has_submenu: true,
|
||||
disabled: false,
|
||||
submenu: {
|
||||
options:apps_opts
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
nodesDivv.appendChild(nodesDiv)
|
||||
nodesDivv.style = `overflow: scroll;
|
||||
height: 70vh;width: 100%;`
|
||||
|
||||
div.appendChild(nodesDivv)
|
||||
|
||||
if (!document.querySelector('#mixlab_find_the_node'))
|
||||
document.body.appendChild(div)
|
||||
}
|
||||
})
|
||||
|
||||
// options.push({
|
||||
// content: `Save For App ♾️Mixlab`,
|
||||
// disabled: false, // or a function determining whether to disable
|
||||
// callback: async () => {
|
||||
|
||||
// }
|
||||
// })
|
||||
return options
|
||||
}
|
||||
return options
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
|
||||
+200
-41
@@ -57,11 +57,35 @@ function hexToRGBA (hexColor) {
|
||||
// 将透明度的十六进制值转换为十进制值
|
||||
var alpha = parseInt(alphaHex, 16) / 255
|
||||
|
||||
return [r,g,b,alpha]
|
||||
return [r, g, b, alpha]
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.Color',
|
||||
init () {
|
||||
$el('link', {
|
||||
rel: 'stylesheet',
|
||||
href: '/extensions/comfyui-mixlab-nodes/lib/classic.min.css',
|
||||
parent: document.head
|
||||
})
|
||||
|
||||
$el('style', {
|
||||
textContent: `
|
||||
.pickr{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.pickr .pcr-button {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
outline: 1px solid white;
|
||||
}
|
||||
|
||||
`,
|
||||
parent: document.body
|
||||
})
|
||||
},
|
||||
async getCustomWidgets (app) {
|
||||
return {
|
||||
TCOLOR (node, inputName, inputData, app) {
|
||||
@@ -75,9 +99,10 @@ app.registerExtension({
|
||||
return [128, 32] // a method to compute the current size of the widget
|
||||
},
|
||||
async serializeValue (nodeId, widgetIndex) {
|
||||
let data = getLocalData('_mixlab_utils_color');
|
||||
let hex=data[node.id] || '#000000'
|
||||
let [r,g,b,a]=hexToRGBA(hex)
|
||||
// let data = getLocalData('_mixlab_utils_color')
|
||||
// let hex = data[node.id] || '#000000'
|
||||
let hex = widget.value || '#000000'
|
||||
let [r, g, b, a] = hexToRGBA(hex)
|
||||
return {
|
||||
hex,
|
||||
r,
|
||||
@@ -100,7 +125,7 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
console.log('Color nodeData', this.widgets)
|
||||
// console.log('Color nodeData', this.widgets)
|
||||
|
||||
const widget = {
|
||||
type: 'div',
|
||||
@@ -110,6 +135,7 @@ app.registerExtension({
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, 44, node.size[1])
|
||||
)
|
||||
// console.log('draw',y,node.widgets[0].last_y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,35 +143,9 @@ app.registerExtension({
|
||||
|
||||
document.body.appendChild(widget.div)
|
||||
|
||||
const inputDiv = (key, placeholder, value) => {
|
||||
const inputDiv = () => {
|
||||
let div = document.createElement('div')
|
||||
const ip = document.createElement('input')
|
||||
ip.type = 'color'
|
||||
ip.className = `${'comfy-multiline-input'} ${placeholder}`
|
||||
div.style = `display: flex;
|
||||
align-items: center;
|
||||
margin: 6px 8px;
|
||||
margin-top: 0;`
|
||||
ip.placeholder = placeholder
|
||||
ip.value = value
|
||||
|
||||
ip.style = `outline: none;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
width: 70%;cursor: pointer;
|
||||
height: 32px;`
|
||||
const label = document.createElement('label')
|
||||
label.style = 'font-size: 10px;min-width:32px'
|
||||
label.innerText = placeholder
|
||||
div.appendChild(label)
|
||||
div.appendChild(ip)
|
||||
|
||||
ip.addEventListener('change', () => {
|
||||
let data = getLocalData(key)
|
||||
data[this.id] = ip.value.trim()
|
||||
localStorage.setItem(key, JSON.stringify(data))
|
||||
// console.log(this.id, ip.value.trim())
|
||||
})
|
||||
div.id = `color_picker_${this.id}`
|
||||
return div
|
||||
}
|
||||
|
||||
@@ -155,10 +155,85 @@ app.registerExtension({
|
||||
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
const pickr = Pickr.create({
|
||||
el: `#${inputColor.id}`,
|
||||
theme: 'classic', // or 'monolith', or 'nano'
|
||||
// closeOnScroll: true,
|
||||
default: '#000000',
|
||||
swatches: [
|
||||
'rgba(244, 67, 54, 1)',
|
||||
'rgba(233, 30, 99, 0.95)',
|
||||
'rgba(156, 39, 176, 0.9)',
|
||||
'rgba(103, 58, 183, 0.85)',
|
||||
'rgba(63, 81, 181, 0.8)',
|
||||
'rgba(33, 150, 243, 0.75)',
|
||||
'rgba(3, 169, 244, 0.7)',
|
||||
'rgba(0, 188, 212, 0.7)',
|
||||
'rgba(0, 150, 136, 0.75)',
|
||||
'rgba(76, 175, 80, 0.8)',
|
||||
'rgba(139, 195, 74, 0.85)',
|
||||
'rgba(205, 220, 57, 0.9)',
|
||||
'rgba(255, 235, 59, 0.95)',
|
||||
'rgba(255, 193, 7, 1)'
|
||||
],
|
||||
|
||||
components: {
|
||||
// Main components
|
||||
preview: true,
|
||||
opacity: true,
|
||||
hue: true,
|
||||
// Input / output Options
|
||||
interaction: {
|
||||
hex: true,
|
||||
rgba: true,
|
||||
hsla: true,
|
||||
hsva: true,
|
||||
cmyk: true,
|
||||
input: true,
|
||||
// clear: true,
|
||||
save: true,
|
||||
cancel: true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
pickr
|
||||
.on('save', (color, instance) => {
|
||||
// console.log('Event: "save"', color.toHEXA().toString())
|
||||
// let data = getLocalData('_mixlab_utils_color')
|
||||
// data[this.id] = color.toHEXA().toString()
|
||||
// localStorage.setItem('_mixlab_utils_color', JSON.stringify(data))
|
||||
|
||||
try {
|
||||
let tc = this.widgets.filter(w => w.type == 'TCOLOR')[0]
|
||||
tc.value = color.toHEXA().toString()
|
||||
} catch (error) {}
|
||||
})
|
||||
.on('cancel', instance => {
|
||||
pickr && pickr.hide()
|
||||
})
|
||||
this.pickr = pickr
|
||||
const handleMouseWheel = () => {
|
||||
try {
|
||||
this.pickr && this.pickr.hide()
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
document.addEventListener('wheel', handleMouseWheel)
|
||||
|
||||
const onRemoved = this.onRemoved
|
||||
this.onRemoved = () => {
|
||||
inputColor.remove()
|
||||
widget.div.remove()
|
||||
|
||||
try {
|
||||
this.pickr.destroyAndRemove()
|
||||
this.pickr = null
|
||||
document.removeEventListener('wheel', handleMouseWheel)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
return onRemoved?.()
|
||||
}
|
||||
|
||||
@@ -171,20 +246,17 @@ app.registerExtension({
|
||||
// You can modify widgets/add handlers/etc here
|
||||
|
||||
if (node.type === 'Color') {
|
||||
let widget = node.widgets.filter(w => w.div)[0]
|
||||
try {
|
||||
let TCOLOR = node.widgets.filter(w => w.type == 'TCOLOR')[0]
|
||||
|
||||
let data = getLocalData('_mixlab_utils_color')
|
||||
|
||||
let id = node.id
|
||||
|
||||
widget.div.querySelector('.Color').value = data[id] || '#000000'
|
||||
setTimeout(() => node.pickr.setColor(TCOLOR.value || '#000000'), 1000)
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.TextToNumber',
|
||||
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass == 'TextToNumber') {
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
@@ -197,9 +269,96 @@ app.registerExtension({
|
||||
const n = this.widgets.filter(w => w.name === 'number')[0]
|
||||
n.value = message.num[0]
|
||||
}
|
||||
|
||||
console.log('TextToNumber', random_number.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const min_max = node => {
|
||||
if(node.widgets){
|
||||
const min_value = node.widgets.filter(w => w.name === 'min_value')[0]
|
||||
const max_value = node.widgets.filter(w => w.name === 'max_value')[0]
|
||||
|
||||
const number = node.widgets.filter(w => w.name === 'number')[0]
|
||||
if (number) {
|
||||
number.options.min = min_value.value
|
||||
number.options.max = max_value.value
|
||||
|
||||
number.value = Math.min(number.options.max, number.value)
|
||||
number.value = Math.max(number.options.min, number.value)
|
||||
}
|
||||
|
||||
if (min_value)
|
||||
min_value.callback = e => {
|
||||
number.options.min = e
|
||||
number.value = e
|
||||
}
|
||||
if (max_value)
|
||||
max_value.callback = e => {
|
||||
number.options.max = e
|
||||
number.value = e
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.FloatSlider',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
|
||||
if (nodeType.comfyClass == 'FloatSlider') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
min_max(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'FloatSlider') {
|
||||
min_max(node)
|
||||
}
|
||||
}
|
||||
})
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.IntNumber',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
|
||||
if (nodeType.comfyClass == 'IntNumber') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
min_max(this)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'IntNumber') {
|
||||
min_max(node)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.TESTNODE_',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
|
||||
if (nodeType.comfyClass == 'TESTNODE_') {
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments);
|
||||
console.log('##',message)
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
@@ -60,30 +60,7 @@ app.registerExtension({
|
||||
type: inputData[0], // the type, CHEESE
|
||||
name: inputName, // the name, slice
|
||||
size: [128, 24], // a default size
|
||||
draw (ctx, node, width, y) {
|
||||
// // 绘制文件图标的函数
|
||||
// function drawFileIcon () {
|
||||
// // 清空画布
|
||||
// // ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
|
||||
// // 绘制文件外框
|
||||
// ctx.fillStyle = '#000'
|
||||
// ctx.fillRect(5, 5, 40, 40)
|
||||
|
||||
// // 绘制文件夹图标
|
||||
// ctx.fillStyle = '#f00'
|
||||
// ctx.fillRect(10, 15, 30, 20)
|
||||
|
||||
// // 绘制监听符号
|
||||
// ctx.beginPath()
|
||||
// ctx.arc(30, 35, 5, 0, 2 * Math.PI)
|
||||
// ctx.fillStyle = '#00f'
|
||||
// ctx.fill()
|
||||
// }
|
||||
|
||||
// // 调用绘制函数
|
||||
// drawFileIcon()
|
||||
},
|
||||
draw (ctx, node, width, y) {},
|
||||
computeSize (...args) {
|
||||
return [128, 24] // a method to compute the current size of the widget
|
||||
},
|
||||
@@ -124,11 +101,19 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
console.log('watch widtget', this.widgets)
|
||||
// 虚拟的widget,用于更新节点,让其每次都运行
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'seed',
|
||||
draw (ctx, node, widget_width, y, widget_height) {}
|
||||
}
|
||||
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
const watcher = this.widgets.filter(w => w.name == 'watcher')[0]
|
||||
|
||||
watcher.callback = () => {
|
||||
console.log('watcher', watcher.value)
|
||||
if (watcher.value === 'enable') {
|
||||
if (window._mixlab_watcher_t)
|
||||
clearInterval(window._mixlab_watcher_t)
|
||||
@@ -140,7 +125,7 @@ app.registerExtension({
|
||||
window._mixlab_file_path_watcher = json.event_type
|
||||
// widget.card.innerText = window._mixlab_file_path_watcher || ''
|
||||
//运行
|
||||
document.querySelector('#queue-button').click()
|
||||
// document.querySelector('#queue-button').click()
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
@@ -162,15 +147,59 @@ app.registerExtension({
|
||||
window._mixlab_file_path_watcher = json.event_type
|
||||
})
|
||||
|
||||
/*
|
||||
Add the widget, make sure we clean up nicely, and we do not want to be serialized!
|
||||
*/
|
||||
// this.addCustomWidget(widget)
|
||||
this.onRemoved = function () {
|
||||
// widget.card.remove()
|
||||
}
|
||||
this.serialize_widgets = true
|
||||
}
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
console.log(message)
|
||||
try {
|
||||
let seed = this.widgets.filter(w => w.name === 'seed')[0]
|
||||
if (seed) {
|
||||
if (!seed.value) seed.value = 0
|
||||
seed.value += 1
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'LoadImagesFromPath') {
|
||||
const watcher = node.widgets.filter(w => w.name == 'watcher')[0]
|
||||
if (watcher) {
|
||||
if (watcher.value === 'enable') {
|
||||
if (window._mixlab_watcher_t) clearInterval(window._mixlab_watcher_t)
|
||||
window._mixlab_watcher_t = setInterval(() => {
|
||||
// 上次路径填充
|
||||
getConfig().then(json => {
|
||||
console.log(json.event_type)
|
||||
if (json.event_type != window._mixlab_file_path_watcher) {
|
||||
window._mixlab_file_path_watcher = json.event_type
|
||||
// widget.card.innerText = window._mixlab_file_path_watcher || ''
|
||||
//运行
|
||||
document.querySelector('#queue-button').click()
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
} else {
|
||||
if (window._mixlab_watcher_t) {
|
||||
clearInterval(window._mixlab_watcher_t)
|
||||
}
|
||||
window._mixlab_watcher_t = null
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let seed = node.widgets.filter(w => w.name === 'seed')[0]
|
||||
if (seed) {
|
||||
if (!seed.value) seed.value = 0
|
||||
seed.value += 1
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Vendored
+2
File diff suppressed because one or more lines are too long
+8
File diff suppressed because one or more lines are too long
+5
File diff suppressed because one or more lines are too long
Vendored
+5
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
/*! PhotoSwipe main CSS by Dmytro Semenov | photoswipe.com */.pswp{--pswp-bg:#000;--pswp-placeholder-bg:#222;--pswp-root-z-index:100000;--pswp-preloader-color:rgba(79, 79, 79, 0.4);--pswp-preloader-color-secondary:rgba(255, 255, 255, 0.9);--pswp-icon-color:#fff;--pswp-icon-color-secondary:#4f4f4f;--pswp-icon-stroke-color:#4f4f4f;--pswp-icon-stroke-width:2px;--pswp-error-text-color:var(--pswp-icon-color)}.pswp{position:fixed;top:0;left:0;width:100%;height:100%;z-index:var(--pswp-root-z-index);display:none;touch-action:none;outline:0;opacity:.003;contain:layout style size;-webkit-tap-highlight-color:transparent}.pswp:focus{outline:0}.pswp *{box-sizing:border-box}.pswp img{max-width:none}.pswp--open{display:block}.pswp,.pswp__bg{transform:translateZ(0);will-change:opacity}.pswp__bg{opacity:.005;background:var(--pswp-bg)}.pswp,.pswp__scroll-wrap{overflow:hidden}.pswp__bg,.pswp__container,.pswp__content,.pswp__img,.pswp__item,.pswp__scroll-wrap,.pswp__zoom-wrap{position:absolute;top:0;left:0;width:100%;height:100%}.pswp__img,.pswp__zoom-wrap{width:auto;height:auto}.pswp--click-to-zoom.pswp--zoom-allowed .pswp__img{cursor:-webkit-zoom-in;cursor:-moz-zoom-in;cursor:zoom-in}.pswp--click-to-zoom.pswp--zoomed-in .pswp__img{cursor:move;cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.pswp--click-to-zoom.pswp--zoomed-in .pswp__img:active{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img,.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img:active,.pswp__img{cursor:-webkit-zoom-out;cursor:-moz-zoom-out;cursor:zoom-out}.pswp__button,.pswp__container,.pswp__counter,.pswp__img{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pswp__item{z-index:1;overflow:hidden}.pswp__hidden{display:none!important}.pswp__content{pointer-events:none}.pswp__content>*{pointer-events:auto}.pswp__error-msg-container{display:grid}.pswp__error-msg{margin:auto;font-size:1em;line-height:1;color:var(--pswp-error-text-color)}.pswp .pswp__hide-on-close{opacity:.005;will-change:opacity;transition:opacity var(--pswp-transition-duration) cubic-bezier(.4,0,.22,1);z-index:10;pointer-events:none}.pswp--ui-visible .pswp__hide-on-close{opacity:1;pointer-events:auto}.pswp__button{position:relative;display:block;width:50px;height:60px;padding:0;margin:0;overflow:hidden;cursor:pointer;background:0 0;border:0;box-shadow:none;opacity:.85;-webkit-appearance:none;-webkit-touch-callout:none}.pswp__button:active,.pswp__button:focus,.pswp__button:hover{transition:none;padding:0;background:0 0;border:0;box-shadow:none;opacity:1}.pswp__button:disabled{opacity:.3;cursor:auto}.pswp__icn{fill:var(--pswp-icon-color);color:var(--pswp-icon-color-secondary)}.pswp__icn{position:absolute;top:14px;left:9px;width:32px;height:32px;overflow:hidden;pointer-events:none}.pswp__icn-shadow{stroke:var(--pswp-icon-stroke-color);stroke-width:var(--pswp-icon-stroke-width);fill:none}.pswp__icn:focus{outline:0}.pswp__img--with-bg,div.pswp__img--placeholder{background:var(--pswp-placeholder-bg)}.pswp__top-bar{position:absolute;left:0;top:0;width:100%;height:60px;display:flex;flex-direction:row;justify-content:flex-end;z-index:10;pointer-events:none!important}.pswp__top-bar>*{pointer-events:auto;will-change:opacity}.pswp__button--close{margin-right:6px}.pswp__button--arrow{position:absolute;top:0;width:75px;height:100px;top:50%;margin-top:-50px}.pswp__button--arrow:disabled{display:none;cursor:default}.pswp__button--arrow .pswp__icn{top:50%;margin-top:-30px;width:60px;height:60px;background:0 0;border-radius:0}.pswp--one-slide .pswp__button--arrow{display:none}.pswp--touch .pswp__button--arrow{visibility:hidden}.pswp--has_mouse .pswp__button--arrow{visibility:visible}.pswp__button--arrow--prev{right:auto;left:0}.pswp__button--arrow--next{right:0}.pswp__button--arrow--next .pswp__icn{left:auto;right:14px;transform:scale(-1,1)}.pswp__button--zoom{display:none}.pswp--zoom-allowed .pswp__button--zoom{display:block}.pswp--zoomed-in .pswp__zoom-icn-bar-v{display:none}.pswp__preloader{position:relative;overflow:hidden;width:50px;height:60px;margin-right:auto}.pswp__preloader .pswp__icn{opacity:0;transition:opacity .2s linear;animation:pswp-clockwise .6s linear infinite}.pswp__preloader--active .pswp__icn{opacity:.85}@keyframes pswp-clockwise{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.pswp__counter{height:30px;margin-top:15px;margin-inline-start:20px;font-size:14px;line-height:30px;color:var(--pswp-icon-color);text-shadow:1px 1px 3px var(--pswp-icon-color-secondary);opacity:.85}.pswp--one-slide .pswp__counter{display:none}
|
||||
Vendored
+3
File diff suppressed because one or more lines are too long
+2423
-705
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,736 @@
|
||||
{
|
||||
"last_node_id": 29,
|
||||
"last_link_id": 32,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 7,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
108,
|
||||
316
|
||||
],
|
||||
"size": {
|
||||
"0": 425.27801513671875,
|
||||
"1": 180.6060791015625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 16
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
6
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"text, watermark"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": "ControlNetApply",
|
||||
"pos": [
|
||||
485,
|
||||
792
|
||||
],
|
||||
"size": {
|
||||
"0": 317.4000244140625,
|
||||
"1": 98
|
||||
},
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 22,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "control_net",
|
||||
"type": "CONTROL_NET",
|
||||
"link": 18,
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 26
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
21
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ControlNetApply"
|
||||
},
|
||||
"widgets_values": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
117,
|
||||
95
|
||||
],
|
||||
"size": {
|
||||
"0": 422.84503173828125,
|
||||
"1": 164.31304931640625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 15
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
22
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"a future city,buiding,future,magic,under water"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "LoraLoader",
|
||||
"pos": [
|
||||
116,
|
||||
-113
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 126
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 13
|
||||
},
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 14
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
12,
|
||||
23
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
15,
|
||||
16
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoraLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"lcm-lora-sdv1-5.safetensors",
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
-380,
|
||||
185
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 98
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
13
|
||||
],
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
14
|
||||
],
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
8
|
||||
],
|
||||
"slot_index": 2
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"deliberate_v2.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"type": "DiffControlNetLoader",
|
||||
"pos": [
|
||||
40,
|
||||
792
|
||||
],
|
||||
"size": {
|
||||
"0": 367.8165283203125,
|
||||
"1": 58.083831787109375
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 23,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONTROL_NET",
|
||||
"type": "CONTROL_NET",
|
||||
"links": [
|
||||
18
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "DiffControlNetLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"control_v11f1p_sd15_depth.pth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
65,
|
||||
573
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
2
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"type": "LeReS-DepthMapPreprocessor",
|
||||
"pos": [
|
||||
47,
|
||||
904
|
||||
],
|
||||
"size": {
|
||||
"0": 369.6000061035156,
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 31
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
26,
|
||||
27
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LeReS-DepthMapPreprocessor"
|
||||
},
|
||||
"widgets_values": [
|
||||
0.1,
|
||||
0,
|
||||
"disable",
|
||||
512
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "KSampler",
|
||||
"pos": [
|
||||
1100,
|
||||
-97
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 12,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 21
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 6
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 2
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
7
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
170633013599955,
|
||||
"fixed",
|
||||
4,
|
||||
1.6,
|
||||
"lcm",
|
||||
"simple",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
1100,
|
||||
-241
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 7
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 8
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
28
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
492,
|
||||
955
|
||||
],
|
||||
"size": {
|
||||
"0": 526.9624633789062,
|
||||
"1": 367.3833923339844
|
||||
},
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 27
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"type": "FloatingVideo",
|
||||
"pos": [
|
||||
1565,
|
||||
-181
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 28
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatingVideo"
|
||||
},
|
||||
"widgets_values": [
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
-241,
|
||||
953
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 32
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"type": "ScreenShare",
|
||||
"pos": [
|
||||
-639,
|
||||
366
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
644
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
31,
|
||||
32
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "PROMPT",
|
||||
"type": "STRING",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "FLOAT",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "INT",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ScreenShare"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
500,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
2,
|
||||
5,
|
||||
0,
|
||||
3,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
6,
|
||||
7,
|
||||
0,
|
||||
3,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
7,
|
||||
3,
|
||||
0,
|
||||
8,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
8,
|
||||
4,
|
||||
2,
|
||||
8,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
12,
|
||||
15,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
13,
|
||||
4,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
14,
|
||||
4,
|
||||
1,
|
||||
15,
|
||||
1,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
15,
|
||||
15,
|
||||
1,
|
||||
6,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
16,
|
||||
15,
|
||||
1,
|
||||
7,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
18,
|
||||
19,
|
||||
0,
|
||||
18,
|
||||
1,
|
||||
"CONTROL_NET"
|
||||
],
|
||||
[
|
||||
21,
|
||||
18,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
22,
|
||||
6,
|
||||
0,
|
||||
18,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
23,
|
||||
15,
|
||||
0,
|
||||
19,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
26,
|
||||
25,
|
||||
0,
|
||||
18,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
27,
|
||||
25,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
28,
|
||||
8,
|
||||
0,
|
||||
26,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
31,
|
||||
29,
|
||||
0,
|
||||
25,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
32,
|
||||
29,
|
||||
0,
|
||||
28,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 23,
|
||||
"last_link_id": 51,
|
||||
"last_node_id": 22,
|
||||
"last_link_id": 48,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 7,
|
||||
@@ -105,7 +105,7 @@
|
||||
{
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"link": 51,
|
||||
"link": 48,
|
||||
"widget": {
|
||||
"name": "text"
|
||||
}
|
||||
@@ -295,7 +295,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
482859286431021,
|
||||
644769503212755,
|
||||
"randomize",
|
||||
4,
|
||||
1.6,
|
||||
@@ -316,19 +316,47 @@
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 50
|
||||
"link": 46
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "FloatingVideo",
|
||||
"pos": [
|
||||
2041,
|
||||
277
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 41
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatingVideo"
|
||||
},
|
||||
"widgets_values": [
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "LoraLoader",
|
||||
@@ -445,13 +473,13 @@
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 49
|
||||
"link": 47
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
@@ -473,43 +501,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "FloatingVideo",
|
||||
"id": 22,
|
||||
"type": "ScreenShare",
|
||||
"pos": [
|
||||
1928,
|
||||
295
|
||||
-111,
|
||||
427
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 41
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatingVideo"
|
||||
},
|
||||
"widgets_values": [
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"type": "ScreenShare",
|
||||
"pos": [
|
||||
-65,
|
||||
446
|
||||
],
|
||||
"size": [
|
||||
312.78457519531213,
|
||||
606.2132135620109
|
||||
644
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
@@ -519,8 +519,8 @@
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
49,
|
||||
50
|
||||
46,
|
||||
47
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
@@ -529,7 +529,7 @@
|
||||
"name": "PROMPT",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
51
|
||||
48
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
@@ -552,7 +552,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
2018,
|
||||
500,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@@ -810,24 +810,24 @@
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
49,
|
||||
23,
|
||||
0,
|
||||
18,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
50,
|
||||
23,
|
||||
46,
|
||||
22,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
51,
|
||||
23,
|
||||
47,
|
||||
22,
|
||||
0,
|
||||
18,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
48,
|
||||
22,
|
||||
1,
|
||||
8,
|
||||
1,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,721 @@
|
||||
{
|
||||
"last_node_id": 23,
|
||||
"last_link_id": 25,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 9,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
2070,
|
||||
830
|
||||
],
|
||||
"size": {
|
||||
"0": 425.27801513671875,
|
||||
"1": 180.6060791015625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 7
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
4
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"text, watermark"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
2070,
|
||||
1060
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
5
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
1640,
|
||||
920
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 98
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
2
|
||||
],
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
6,
|
||||
7
|
||||
],
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
9
|
||||
],
|
||||
"slot_index": 2
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"deliberate_v2.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
2080,
|
||||
630
|
||||
],
|
||||
"size": {
|
||||
"0": 422.84503173828125,
|
||||
"1": 164.31304931640625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 6
|
||||
},
|
||||
{
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"link": 16,
|
||||
"widget": {
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
3
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": "ShowTextForGPT",
|
||||
"pos": [
|
||||
3288,
|
||||
-323
|
||||
],
|
||||
"size": {
|
||||
"0": 449.1168212890625,
|
||||
"1": 76
|
||||
},
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"link": 20,
|
||||
"widget": {
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": null,
|
||||
"shape": 6
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ShowTextForGPT"
|
||||
},
|
||||
"widgets_values": [
|
||||
"a girl face,super,(Pop Art:1.26),(Black and White:1.26)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "KSampler",
|
||||
"pos": [
|
||||
2520,
|
||||
630
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 2
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 3
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 4
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 5
|
||||
},
|
||||
{
|
||||
"name": "seed",
|
||||
"type": "INT",
|
||||
"link": 21,
|
||||
"widget": {
|
||||
"name": "seed"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
8
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
1063141893699118,
|
||||
"fixed",
|
||||
20,
|
||||
8,
|
||||
"euler",
|
||||
"normal",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": "IntNumber",
|
||||
"pos": [
|
||||
2852,
|
||||
275
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "INT",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
21
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"title": "Seed",
|
||||
"properties": {
|
||||
"Node name for S&R": "IntNumber"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"type": "PromptSlide",
|
||||
"pos": [
|
||||
2364,
|
||||
-294
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
23
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PromptSlide"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Pop Art",
|
||||
1.26,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"type": "PromptSlide",
|
||||
"pos": [
|
||||
2360,
|
||||
-544
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
22
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PromptSlide"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Black and White",
|
||||
1.26,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"type": "RandomPrompt",
|
||||
"pos": [
|
||||
2840,
|
||||
-330
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 224
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "mutable_prompt",
|
||||
"type": "STRING",
|
||||
"link": 22,
|
||||
"widget": {
|
||||
"name": "mutable_prompt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "immutable_prompt",
|
||||
"type": "STRING",
|
||||
"link": 23,
|
||||
"widget": {
|
||||
"name": "immutable_prompt"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
19
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "RandomPrompt"
|
||||
},
|
||||
"widgets_values": [
|
||||
1,
|
||||
"",
|
||||
" ``",
|
||||
"disable",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "RandomPrompt",
|
||||
"pos": [
|
||||
2840,
|
||||
-30
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 224
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "mutable_prompt",
|
||||
"type": "STRING",
|
||||
"link": 19,
|
||||
"widget": {
|
||||
"name": "mutable_prompt"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
16,
|
||||
20
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "RandomPrompt"
|
||||
},
|
||||
"widgets_values": [
|
||||
1,
|
||||
"",
|
||||
" a girl face,super,``",
|
||||
"disable",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
3449,
|
||||
-102
|
||||
],
|
||||
"size": {
|
||||
"0": 435.8727111816406,
|
||||
"1": 511.8609619140625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 24
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
2870,
|
||||
630
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 8
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 9
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
24,
|
||||
25
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"type": "AppInfo",
|
||||
"pos": [
|
||||
3363.0014990624995,
|
||||
551.2261418945309
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 344
|
||||
},
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "LOGO",
|
||||
"type": "IMAGE",
|
||||
"link": 25
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "AppInfo"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Prompt-weight-test",
|
||||
"21\n22\n18",
|
||||
"11\n16",
|
||||
"",
|
||||
1,
|
||||
"",
|
||||
"https://",
|
||||
"",
|
||||
"enable",
|
||||
1
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
2,
|
||||
6,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
3,
|
||||
8,
|
||||
0,
|
||||
5,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
4,
|
||||
9,
|
||||
0,
|
||||
5,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
5,
|
||||
7,
|
||||
0,
|
||||
5,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
6,
|
||||
6,
|
||||
1,
|
||||
8,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
7,
|
||||
6,
|
||||
1,
|
||||
9,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
8,
|
||||
5,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
9,
|
||||
6,
|
||||
2,
|
||||
10,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
16,
|
||||
15,
|
||||
0,
|
||||
8,
|
||||
1,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
19,
|
||||
13,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
20,
|
||||
15,
|
||||
0,
|
||||
16,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
21,
|
||||
18,
|
||||
0,
|
||||
5,
|
||||
4,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
22,
|
||||
21,
|
||||
0,
|
||||
13,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
23,
|
||||
22,
|
||||
0,
|
||||
13,
|
||||
1,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
24,
|
||||
10,
|
||||
0,
|
||||
11,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
25,
|
||||
10,
|
||||
0,
|
||||
23,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
+166
-164
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 25,
|
||||
"last_link_id": 32,
|
||||
"last_node_id": 26,
|
||||
"last_link_id": 34,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 5,
|
||||
@@ -41,48 +41,6 @@
|
||||
"text, watermark"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
1867,
|
||||
-2378
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 7
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 8
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
9
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "EnhanceImage",
|
||||
@@ -101,7 +59,7 @@
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 25
|
||||
"link": 34
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
@@ -122,57 +80,6 @@
|
||||
1.1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
998,
|
||||
-2674
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
106
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 30,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 32,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
26
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"type": "LimitNumber",
|
||||
@@ -262,7 +169,7 @@
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
@@ -283,7 +190,10 @@
|
||||
"Node name for S&R": "IntNumber"
|
||||
},
|
||||
"widgets_values": [
|
||||
512
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -295,7 +205,7 @@
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
@@ -316,7 +226,10 @@
|
||||
"Node name for S&R": "IntNumber"
|
||||
},
|
||||
"widgets_values": [
|
||||
512
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -428,7 +341,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
730250870715434,
|
||||
52336089190516,
|
||||
"randomize",
|
||||
15,
|
||||
6.9,
|
||||
@@ -437,39 +350,6 @@
|
||||
0.59
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "FloatSlider",
|
||||
"pos": [
|
||||
1007,
|
||||
-2819
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "FLOAT",
|
||||
"type": "FLOAT",
|
||||
"links": [
|
||||
16
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"title": "denoise",
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatSlider"
|
||||
},
|
||||
"widgets_values": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": "PreviewImage",
|
||||
@@ -535,24 +415,31 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "AppInfo",
|
||||
"id": 6,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
2134,
|
||||
-2528
|
||||
1867,
|
||||
-2378
|
||||
],
|
||||
"size": {
|
||||
"0": 408.4201965332031,
|
||||
"1": 406.9195861816406
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 9
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 7
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 8
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
@@ -560,12 +447,37 @@
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
25
|
||||
33,
|
||||
34
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"type": "AppInfo",
|
||||
"pos": [
|
||||
2134,
|
||||
-2264
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 344.00006103515625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "LOGO",
|
||||
"type": "IMAGE",
|
||||
"link": 33
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "AppInfo"
|
||||
},
|
||||
@@ -576,7 +488,97 @@
|
||||
"演示基本的文生图流程",
|
||||
1,
|
||||
"#comfyui-mixlab-nodes# ",
|
||||
null
|
||||
"https://",
|
||||
"",
|
||||
"enable",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "FloatSlider",
|
||||
"pos": [
|
||||
1007,
|
||||
-2860
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "FLOAT",
|
||||
"type": "FLOAT",
|
||||
"links": [
|
||||
16
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"title": "denoise",
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatSlider"
|
||||
},
|
||||
"widgets_values": [
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0.001
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
1004,
|
||||
-2633
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 30,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 32,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
26
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -637,14 +639,6 @@
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
9,
|
||||
6,
|
||||
0,
|
||||
7,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
16,
|
||||
14,
|
||||
@@ -661,14 +655,6 @@
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
25,
|
||||
7,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
26,
|
||||
21,
|
||||
@@ -708,6 +694,22 @@
|
||||
21,
|
||||
1,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
33,
|
||||
6,
|
||||
0,
|
||||
26,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
34,
|
||||
6,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 2.5 MiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 133,
|
||||
"last_link_id": 289,
|
||||
"last_node_id": 134,
|
||||
"last_link_id": 296,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 42,
|
||||
@@ -178,7 +178,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
487152733962296,
|
||||
981322491518329,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -284,7 +284,7 @@
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 91,
|
||||
"link": 291,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
@@ -292,7 +292,7 @@
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 92,
|
||||
"link": 292,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
@@ -554,7 +554,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
1031291986804327,
|
||||
836429510062488,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -824,7 +824,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
715240655341851,
|
||||
24900528323969,
|
||||
"randomize",
|
||||
12,
|
||||
8,
|
||||
@@ -875,67 +875,6 @@
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"type": "NewLayer",
|
||||
"pos": [
|
||||
1012.7987748686522,
|
||||
1330
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 32,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 78
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 79
|
||||
},
|
||||
{
|
||||
"name": "layers",
|
||||
"type": "LAYER",
|
||||
"link": 227,
|
||||
"slot_index": 2
|
||||
},
|
||||
{
|
||||
"name": "canvas",
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "layers",
|
||||
"type": "LAYER",
|
||||
"links": [
|
||||
228
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "NewLayer"
|
||||
},
|
||||
"widgets_values": [
|
||||
179,
|
||||
134,
|
||||
156,
|
||||
146,
|
||||
4,
|
||||
"width",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 65,
|
||||
"type": "NewLayer",
|
||||
@@ -1221,42 +1160,6 @@
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"type": "Reroute",
|
||||
"pos": [
|
||||
616,
|
||||
1515
|
||||
],
|
||||
"size": [
|
||||
75,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "*",
|
||||
"link": 48
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
90
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"showOutputText": false,
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 96,
|
||||
"type": "Reroute",
|
||||
@@ -1294,55 +1197,6 @@
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 49,
|
||||
"type": "GetImageSize",
|
||||
"pos": [
|
||||
999,
|
||||
1189
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 24,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 90
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
91,
|
||||
238,
|
||||
248
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
92,
|
||||
239,
|
||||
249
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "GetImageSize"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 118,
|
||||
"type": "NewLayer",
|
||||
@@ -1376,7 +1230,7 @@
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 248,
|
||||
"link": 295,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
@@ -1384,7 +1238,7 @@
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 249,
|
||||
"link": 296,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
@@ -1572,7 +1426,7 @@
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 238,
|
||||
"link": 293,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
@@ -1580,7 +1434,7 @@
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 239,
|
||||
"link": 294,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
@@ -1955,7 +1809,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
397643785545040,
|
||||
371831550273153,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -2164,7 +2018,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
590319162197911,
|
||||
996820396025175,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -2310,7 +2164,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
457217754867837,
|
||||
763274893004397,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -2780,7 +2634,7 @@
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 234
|
||||
"1": 240
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
@@ -2814,7 +2668,8 @@
|
||||
100,
|
||||
13,
|
||||
null,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2986,10 +2841,10 @@
|
||||
100,
|
||||
706
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
234
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 234
|
||||
},
|
||||
"flags": {},
|
||||
"order": 15,
|
||||
"mode": 0,
|
||||
@@ -3034,7 +2889,8 @@
|
||||
100,
|
||||
13,
|
||||
null,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3044,10 +2900,10 @@
|
||||
100,
|
||||
1330
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
234
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 234
|
||||
},
|
||||
"flags": {},
|
||||
"order": 16,
|
||||
"mode": 0,
|
||||
@@ -3091,7 +2947,8 @@
|
||||
100,
|
||||
13,
|
||||
null,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3101,10 +2958,10 @@
|
||||
100,
|
||||
1922
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
234
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 234
|
||||
},
|
||||
"flags": {},
|
||||
"order": 17,
|
||||
"mode": 0,
|
||||
@@ -3147,7 +3004,8 @@
|
||||
100,
|
||||
13,
|
||||
null,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3159,7 +3017,7 @@
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 70
|
||||
"1": 174
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
@@ -3175,13 +3033,37 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "r",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "g",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "a",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "Color"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
"#000000",
|
||||
null
|
||||
]
|
||||
},
|
||||
@@ -3221,6 +3103,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -3263,6 +3151,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -3306,6 +3200,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -3334,6 +3234,152 @@
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 134,
|
||||
"type": "GetImageSize_",
|
||||
"pos": [
|
||||
734,
|
||||
1628
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 24,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 290
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
291,
|
||||
293,
|
||||
295
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
292,
|
||||
294,
|
||||
296
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "GetImageSize_"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"type": "Reroute",
|
||||
"pos": [
|
||||
597,
|
||||
1627
|
||||
],
|
||||
"size": [
|
||||
75,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "*",
|
||||
"link": 48
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
290
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"showOutputText": false,
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"type": "NewLayer",
|
||||
"pos": [
|
||||
1056,
|
||||
1247
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 32,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 78
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 79
|
||||
},
|
||||
{
|
||||
"name": "layers",
|
||||
"type": "LAYER",
|
||||
"link": 227,
|
||||
"slot_index": 2
|
||||
},
|
||||
{
|
||||
"name": "canvas",
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "layers",
|
||||
"type": "LAYER",
|
||||
"links": [
|
||||
228
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "NewLayer"
|
||||
},
|
||||
"widgets_values": [
|
||||
179,
|
||||
134,
|
||||
156,
|
||||
146,
|
||||
4,
|
||||
"width",
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
@@ -3433,30 +3479,6 @@
|
||||
1,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
90,
|
||||
28,
|
||||
0,
|
||||
49,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
91,
|
||||
49,
|
||||
0,
|
||||
50,
|
||||
0,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
92,
|
||||
49,
|
||||
1,
|
||||
50,
|
||||
1,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
111,
|
||||
63,
|
||||
@@ -4001,22 +4023,6 @@
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
238,
|
||||
49,
|
||||
0,
|
||||
116,
|
||||
3,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
239,
|
||||
49,
|
||||
1,
|
||||
116,
|
||||
4,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
247,
|
||||
116,
|
||||
@@ -4025,22 +4031,6 @@
|
||||
2,
|
||||
"LAYER"
|
||||
],
|
||||
[
|
||||
248,
|
||||
49,
|
||||
0,
|
||||
118,
|
||||
3,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
249,
|
||||
49,
|
||||
1,
|
||||
118,
|
||||
4,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
251,
|
||||
119,
|
||||
@@ -4312,6 +4302,62 @@
|
||||
119,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
290,
|
||||
28,
|
||||
0,
|
||||
134,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
291,
|
||||
134,
|
||||
0,
|
||||
50,
|
||||
0,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
292,
|
||||
134,
|
||||
1,
|
||||
50,
|
||||
1,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
293,
|
||||
134,
|
||||
0,
|
||||
116,
|
||||
3,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
294,
|
||||
134,
|
||||
1,
|
||||
116,
|
||||
4,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
295,
|
||||
134,
|
||||
0,
|
||||
118,
|
||||
3,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
296,
|
||||
134,
|
||||
1,
|
||||
118,
|
||||
4,
|
||||
"INT"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg id="Layer_1" style="enable-background:new 0 0 50 50;" version="1.1" viewBox="0 0 50 50" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="Layer_1_1_"><path d="M18.293,31.707h6.414l24-24l-6.414-6.414l-24,24V31.707z M45.879,7.707l-3.586,3.586l-3.586-3.586l3.586-3.586 L45.879,7.707z M20.293,26.121l17-17l3.586,3.586l-17,17h-3.586V26.121z"/><polygon points="43.293,19.707 41.293,19.707 41.293,46.707 3.293,46.707 3.293,8.707 31.293,8.707 31.293,6.707 1.293,6.707 1.293,48.707 43.293,48.707 "/></g></svg>
|
||||
|
After Width: | Height: | Size: 589 B |
Reference in New Issue
Block a user