Compare commits

..
29 Commits
Author SHA1 Message Date
shadowcz007 e06e032701 fixbug 2023-12-31 23:56:30 +08:00
shadowcz007 c9499c2c79 update 2023-12-31 23:43:17 +08:00
shadowcz007 2bf43541bb Update appinfo-workflow.svg 2023-12-31 23:42:23 +08:00
shadowcz007 0386c7266d Update app_mixlab.js 2023-12-31 23:41:12 +08:00
shadowcz007 7aa6ed9a5d fixbug 2023-12-31 23:36:57 +08:00
shadowcz007 b71325afa5 fixbug 2023-12-31 22:50:40 +08:00
shadowcz007 2cc29bdf77 适配了最新版comfyui的py3.11 ,torch 2.1.2+cu121 2023-12-31 22:48:11 +08:00
shadowcz007 b5d602abc4 Update index.html 2023-12-31 22:01:26 +08:00
shadowcz007 33c45637ac app 2023-12-31 21:39:50 +08:00
shadowcz007 662d4478d0 Update ui_mixlab.js 2023-12-31 21:31:02 +08:00
shadowcz007 31d3809572 Update ui_mixlab.js 2023-12-31 21:28:45 +08:00
shadowcz007 024ff4a309 Update Lama.py 2023-12-31 21:24:40 +08:00
shadowcz007 9ae8d30b6b llma 2023-12-31 21:23:26 +08:00
shadowcz007 44349c10b0 Update index.html 2023-12-31 20:09:34 +08:00
shadowcz007 506520a3c4 Create Prompt-weight-workflow.json 2023-12-31 17:58:06 +08:00
shadowcz007 50f7020977 prompt-weight 2023-12-31 17:50:47 +08:00
shadowcz007 02a27a03cc Update PromptNode.py 2023-12-31 16:22:57 +08:00
shadowcz007 4ad6bacf7b 优化 2023-12-31 16:20:36 +08:00
shadowcz007 26ecc0fa44 新增 PromptSlide节点,实现滑块调节prompt的权重 2023-12-31 16:04:30 +08:00
shadowcz007 2619befca6 Update README.md 2023-12-31 13:14:29 +08:00
shadowcz007 ea24f52b13 Update checkVersion_mixlab.js 2023-12-31 13:12:34 +08:00
shadowcz007 4abfc47346 ### Update 0.8.0
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
2023-12-31 13:11:31 +08:00
shadowcz007 3b95010d06 新增LaMaInpainting & 优化color节点的输出 2023-12-31 13:04:28 +08:00
shadow b3c1b96088 Merge pull request #97 from shadowcz007/fix_hidpi_node_move_center
Fix:node can't move to center on HiDPI device
2023-12-31 10:07:07 +08:00
shadowcz007 a9f1326873 update 2023-12-30 23:52:50 +08:00
shadowcz007 75a696fb64 update 2023-12-30 23:39:36 +08:00
shadowcz007 24aaacba6d update 2023-12-30 23:38:52 +08:00
shadowcz007 e3cd7d5f91 更新示例 2023-12-30 21:05:07 +08:00
gold3bear 6359c3f70f Fix:node can't move to center on HiDPI device 2023-12-30 02:09:45 +08:00
21 changed files with 2026 additions and 154 deletions
+46 -24
View File
@@ -1,22 +1,29 @@
##
v0.7.0 🚀🚗🚚🏃‍ Workflow-to-APP
- 支持多个web app 切换
> 适配了最新版comfyui的py3.11 ,torch 2.1.2+cu121
## 🚀🚗🚚🏃 Workflow-to-APP
- 新增AppInfo节点,可以通过简单的配置,把workflow转变为一个Web APP。
- 支持多个web app 切换
- 发布为app的workflow,可以在右键里再次编辑了
- 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.
![](./assets/0-m-app.png)
![](./assets/appinfo-readme.png)
![](./assets/appinfo-2.png)
Example:
- workflow
![APP info](./workflow/appinfo-workflow.svg)
[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
@@ -24,15 +31,9 @@ APP-JSON:
> 输出节点:PreviewImage 、SaveImage、ShowTextForGPT、VHS_VideoCombine
### 3D
![](./assets/3dimage.png)
[workflow](./workflow/3D-workflow.json)
## 🏃🚗🚚🚀 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! 💻🌐
### 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! 💻🌐
>
![screenshare](./assets/screenshare.png)
https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43e-410a-ab3a-1952b7b4e7da
@@ -43,6 +44,7 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
!! Please use the address with HTTPS (https://127.0.0.1).
### SpeechRecognition & SpeechSynthesis
![f](./assets/audio-workflow.svg)
@@ -51,11 +53,23 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
### GPT
> Support for calling multiple GPTs.ChatGPT、ChatGLM3 , Some code provided by rui. If you are using OpenAI's service, fill in https://api.openai.com/v1 . If you are using a local LLM service, fill in http://127.0.0.1:xxxx/v1 . Azure OpenAI:https://xxxx.openai.azure.com
![gpt-workflow.svg](./assets/gpt-workflow.svg)
[workflow-5](./workflow/5-gpt-workflow.json)
### 3D
![](./assets/3dimage.png)
[workflow](./workflow/3D-workflow.json)
### Layers
> A new layer class node has been added, allowing you to separate the image into layers. After merging the images, you can input the controlnet for further processing.
![layers](./assets/layers-workflow.svg)
![poster](./assets/poster-workflow.svg)
### 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.
@@ -67,13 +81,6 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
> Conveniently load images from a fixed address on the internet to ensure that default images in the workflow can be executed.
### Layers
> A new layer class node has been added, allowing you to separate the image into layers. After merging the images, you can input the controlnet for further processing.
![layers](./assets/layers-workflow.svg)
![poster](./assets/poster-workflow.svg)
## Utils
> The Color node provides a color picker for easy color selection, the Font node offers built-in font selection for use with TextImage to generate text images, and the DynamicDelayByText node allows delayed execution based on the length of the input text.
@@ -113,6 +120,10 @@ Add edges to an image.
![FeatheredMask](./assets/FlVou_Y6kaGWYoEj1Tn0aTd4AjMI.jpg)
> LaMaInpainting
from [simple-lama-inpainting](https://github.com/enesmsahin/simple-lama-inpainting)
### Improvement
@@ -126,14 +137,26 @@ An improvement has been made to directly redirect to GitHub to search for missin
![node-not-found](./assets/node-not-found.png)
### 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 : model/clipseg
[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) -->
## Installation
manually install, simply clone the repo into the custom_nodes directory with this command:
@@ -167,7 +190,6 @@ pip3 install -r requirements.txt
访问 [www.mixcomfy.com](https://www.mixcomfy.com),获得更多内测功能,关注微信公众号:Mixlab无界社区
#### Thanks:
[ComfyUI-CLIPSeg](https://github.com/biegert/ComfyUI-CLIPSeg/tree/main)
+9 -5
View File
@@ -221,7 +221,7 @@ def get_my_workflow_for_app(filename="my_workflow_app.json"):
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"]:
apps.append({
"filename":item["filename"],
@@ -437,7 +437,7 @@ PromptServer.add_routes=new_add_routes
# 导入节点
from .nodes.PromptNode import RandomPrompt
from .nodes.PromptNode import RandomPrompt,PromptSlide
from .nodes.ImageNode import NoiseImage,TransparentImage,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
@@ -445,13 +445,14 @@ 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
# 要导出的所有节点及其名称的字典
# 注意:名称应全局唯一
NODE_CLASS_MAPPINGS = {
"AppInfo":AppInfo,
"RandomPrompt":RandomPrompt,
"PromptSlide":PromptSlide,
"NoiseImage":NoiseImage,
"TransparentImage":TransparentImage,
"ResizeImageMixlab":ResizeImage,
@@ -492,6 +493,7 @@ NODE_CLASS_MAPPINGS = {
"GetImageSize_":GetImageSize_,
"SwitchByIndex":SwitchByIndex,
"LimitNumber":LimitNumber,
"LaMaInpainting":LaMaInpainting
# "GamePal":GamePal
}
@@ -511,7 +513,9 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"SpeechSynthesis":"SpeechSynthesis ♾️Mixlab",
"SpeechRecognition":"SpeechRecognition ♾️Mixlab",
"3DImage":"3DImage ♾️Mixlab",
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab"
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab",
"LaMaInpainting":"LaMaInpainting ♾️Mixlab",
"PromptSlide":"PromptSlide ♾️Mixlab"
# "GamePal":"GamePal ♾️Mixlab"
}
@@ -520,5 +524,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
WEB_DIRECTORY = "./web"
print('--------------')
print('\033[91mMixlab Nodes: \033[93mLoaded\033[0m')
print('\033[91m ### Mixlab Nodes: \033[93mLoaded\033[0m')
print('--------------')
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+4 -1
View File
@@ -4784,6 +4784,7 @@
"MergeLayers",
"NewLayer",
"RandomPrompt",
"PromptSlide",
"ScreenShare",
"ShowLayer",
"ShowTextForGPT",
@@ -4793,12 +4794,14 @@
"SplitLongMask",
"SvgImage",
"TextImage",
"ResizeImageMixlab",
"TransparentImage",
"VAEDecodeConsistencyDecoder",
"VAELoaderConsistencyDecoder",
"TextToNumber",
"TextInput_",
"DynamicDelayProcessor"
"DynamicDelayProcessor",
"LaMaInpainting"
],
{
"title_aux": "comfyui-mixlab-nodes"
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
View File
@@ -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))
@@ -101,7 +101,7 @@ class CLIPSeg:
return {"required":
{
"image": ("IMAGE",),
"text": ("STRING", {"multiline": False}),
"text": ("STRING", {"multiline": False,"dynamicPrompts": False}),
},
"optional":
@@ -246,7 +246,7 @@ class CombineMasks:
# Resize heatmap and binary mask to match the original image dimensions
dimensions = (image_np.shape[1], image_np.shape[0])
print('heatmap',heatmap)
# print('heatmap',heatmap)
if dimensions is None or dimensions[0] == 0 or dimensions[1] == 0:
raise ValueError("Invalid dimensions")
+7 -6
View File
@@ -506,6 +506,7 @@ def generate_text_image(text, font_path, font_size, text_color, vertical=True, s
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
@@ -514,10 +515,10 @@ 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)) * (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) * (font_size + spacing)) + spacing
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))
@@ -990,8 +991,8 @@ class TextImage:
def INPUT_TYPES(s):
return {"required": {
"text": ("STRING",{"multiline": True,"default": "龍馬精神迎新歲"}),
"font_path": ("STRING",{"multiline": False,"default": FONT_PATH}),
"text": ("STRING",{"multiline": True,"default": "龍馬精神迎新歲","dynamicPrompts": False}),
"font_path": ("STRING",{"multiline": False,"default": FONT_PATH,"dynamicPrompts": False}),
"font_size": ("INT",{
"default":100,
"min": 100, #Minimum value
@@ -1001,12 +1002,12 @@ class TextImage:
}),
"spacing": ("INT",{
"default":12,
"min": 1, #Minimum value
"min": -200, #Minimum value
"max": 200, #Maximum value
"step": 1, #Slider's step
"display": "number" # Cosmetic only: display as "number" or "slider"
}),
"text_color":("STRING",{"multiline": False,"default": "#000000"}),
"text_color":("STRING",{"multiline": False,"default": "#000000","dynamicPrompts": False}),
"vertical":("BOOLEAN", {"default": True},),
},
}
+84
View File
@@ -0,0 +1,84 @@
import os
import folder_paths
from simple_lama_inpainting import SimpleLama
from PIL import Image
import numpy as np
import torch
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):
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)
# simple_lama = SimpleLama()
# img_path = "image.png"
# mask_path = "mask.png"
# image = Image.open(img_path)
# mask = Image.open(mask_path).convert('L')
# result = simple_lama(image, mask)
# result.save("inpainted.png")
class LaMaInpainting:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"image": ("IMAGE",),
"mask": ("MASK",),
},
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
global simple_lama
simple_lama = None
def run(self,image,mask):
global simple_lama
result=[]
if simple_lama==None:
simple_lama = SimpleLama()
else:
simple_lama.model.to("cuda" if torch.cuda.is_available() else "cpu")
for i in range(len(image)):
im=image[i]
ma=mask[i]
im=tensor2pil(im)
ma=tensor2pil(ma)
ma =ma.convert('L')
res = simple_lama(im, ma)
res=pil2tensor(res)
result.append(res)
# result.save("inpainted.png")
if simple_lama.device=='cuda':
simple_lama.model.to('cpu')
return (result,)
+120 -53
View File
@@ -4,12 +4,11 @@ import json
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)
# 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 +44,72 @@ default_prompt1='''Swing
'''
default_prompt1="\n".join([p.strip() for p in default_prompt1.split('\n') if p.strip()!=''])
def addWeight(text, weight=1):
if weight == 1:
return text
else:
return f"({text}:{round(weight,2)})"
class PromptSlide:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"prompt_keyword": ("STRING",
{
"multiline": False,
"default": ''
}),
"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:
'''
@@ -87,7 +152,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 +171,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)
@@ -126,62 +193,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'],)}
+68 -14
View File
@@ -88,18 +88,23 @@ class ColorInput:
},
}
RETURN_TYPES = ("STRING",)
# RETURN_NAMES = ("WIDTH","HEIGHT","X","Y",)
RETURN_TYPES = ("STRING","INT","INT","INT","FLOAT",)
RETURN_NAMES = ("hex","r","g","b","a",)
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,False,)
OUTPUT_IS_LIST = (False,False,False,False,False,)
def run(self,color):
return (color,)
h=color['hex']
r=color['r']
g=color['g']
b=color['b']
a=color['a']
return (h,r,g,b,a,)
@@ -120,10 +125,10 @@ class FontInput:
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:
@@ -177,6 +182,27 @@ class FloatSlider:
"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,8 +215,11 @@ class FloatSlider:
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,)
@@ -202,9 +231,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,8 +267,11 @@ class IntNumber:
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:
@@ -385,6 +438,7 @@ class AppInfo:
"display": "number"
}),
"share_prefix":("STRING",{"multiline": False,"default": "","dynamicPrompts": False}),
"link":("STRING",{"multiline": False,"default": "https://","dynamicPrompts": False}),
}
}
@@ -399,13 +453,13 @@ class AppInfo:
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
def run(self,name,image,input_ids,output_ids,description,version,share_prefix):
def run(self,name,image,input_ids,output_ids,description,version,share_prefix,link):
im=create_temp_file(image)
# 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]}, "result": (image,)}
+2 -1
View File
@@ -3,4 +3,5 @@ pyOpenSSL
watchdog
opencv-python-headless
matplotlib
openai
openai
simple-lama-inpainting
+103 -22
View File
@@ -11,6 +11,7 @@
width: 90%;
min-width: 400px;
margin-left: 5%;
user-select: none;
}
.apps {
@@ -85,6 +86,17 @@
}
.status_seed {
display: flex;
flex-direction: column;
margin: 12px;
}
.seeds {
font-size: 12px;
margin-top: 4px;
}
.status {
background: black;
color: white;
@@ -92,7 +104,7 @@
width: fit-content;
padding: 4px;
font-size: 12px;
margin: 12px;
}
.description {
@@ -143,6 +155,7 @@
.input_card {
display: flex;
flex-direction: column;
width: 100%;
}
.output {
@@ -153,7 +166,7 @@
.output_card {
height: 100%;
width: 100%;
box-shadow: 0px 0px 20px 7px #e6e7e7;
margin-top: 24px;
display: flex;
flex-wrap: wrap;
@@ -168,6 +181,7 @@
max-height: 600px;
margin: 8px;
min-width: 200px;
box-shadow: 0px 0px 20px 7px #e6e7e7;
}
.card {
@@ -276,6 +290,7 @@
outline: none;
border: none;
border-radius: 4px;
margin-top: 8px;
}
</style>
<!-- <script src="../../../scripts/api.js" type="module"></script> -->
@@ -346,7 +361,7 @@
function queuePrompt(promptWorkflow, client_id) {
// 随机seed
promptWorkflow = randomSeed(promptWorkflow);
// promptWorkflow = randomSeed(promptWorkflow);
let url = get_url()
const data = JSON.stringify({ prompt: promptWorkflow, client_id });
@@ -375,9 +390,7 @@
}
async function get_my_app(filename = null) {
let url = get_url()
const res = await fetch(`${url}/mixlab/workflow`, {
method: 'POST',
body: JSON.stringify({
@@ -397,15 +410,12 @@
})
}
} catch (error) {
}
return data
}
function createOutputs(outputData) {
function createOutputs(outputData, link) {
const container = document.createElement('div');
container.className = "output";
@@ -426,12 +436,23 @@
action.appendChild(copyText)
copyText.style.marginLeft = '18px';
if (link) {
const linkBtn = document.createElement('button');
// linkBtn.href = link;
linkBtn.innerText = 'go to'
action.appendChild(linkBtn)
linkBtn.style.marginLeft = '18px';
linkBtn.addEventListener('click', e => {
e.preventDefault();
window.open(link);
})
}
const output_card = document.createElement("div");
output_card.className = 'output_card'
container.appendChild(output_card)
copyText.addEventListener('click', e => {
e.preventDefault();
copyTextToClipboard((window._appData.share_prefix || '') + " " + output_card.outerHTML, (r) => success(r, copyText, 'copy text for share'))
@@ -677,7 +698,10 @@
// class_type: "LoadImage"
// }
// ];
inputData = inputData.filter(inp => inp);
// console.log('inputData',inputData)
inputData.forEach(data => {
// console.log(data)
// Check if the class_type is "LoadImage"
if (data.class_type === "LoadImage") {
// Create a container for the upload control
@@ -760,15 +784,40 @@
container.appendChild(uploadContainer);
}
if (['FloatSlider', 'IntNumber'].includes(data.class_type)) {
if (["PromptSlide"].includes(data.class_type)) {
// 滑块输入
let options = data.options || {
min: -3,
max: 3,
};
const label = data.title == 'PromptSlide ♾️Mixlab' ? data.inputs.prompt_keyword : data.title
let silde = createNumSlide(label,
data.inputs.weight,
(v) => {
window._appData.data[data.id].inputs.weight = v;
},
options.min,
options.max,
'float')
container.appendChild(silde);
}
if (['FloatSlider', 'IntNumber'].includes(data.class_type)) {
// console.log('data.options',data.options)
// 滑块输入
let options = data.options || {
min: 0,
max: data.class_type === 'IntNumber' ? 6000 : 1,
};
let silde = createNumSlide(data.title,
data.inputs.number,
(v) => {
window._appData.data[data.id].inputs.number = v;
},
0,
data.class_type === 'IntNumber' ? 6000 : 1,
options.min,
options.max,
data.class_type === 'IntNumber' ? 'int' : 'float')
container.appendChild(silde);
}
@@ -914,10 +963,15 @@
function createUI(inputData, outputData, share = true) {
function createUI(inputData, outputData, seed = {}, share = true, link = "") {
let mainDiv = document.createElement('div');
let leftDiv = document.createElement('div');
let leftDetails = document.createElement('details');
leftDetails.setAttribute('open', 'true')
leftDetails.innerHTML = `<summary>INPUT</summary>
<div class="content"></div>`
let leftDiv = leftDetails.querySelector('.content');
let rightDiv = document.createElement('div');
mainDiv.className = 'app'
@@ -925,7 +979,7 @@
leftDiv.style.alignItems = 'flex-start';
rightDiv.className = 'panel'
leftDiv.style.flex = 0.4
rightDiv.style.flex = 0.6;
rightDiv.style.flex = 1;
// rightDiv.style.height='70vh'
// rightDiv.style=`position: fixed;
// right: 0;
@@ -967,14 +1021,41 @@
iconDes.className = 'description'
// 创建状态标签
let statusDiv = document.createElement('div');
statusDiv.className = 'status_seed'
var status = document.createElement('div');
status.textContent = 'Status';
status.className = 'status';
// seed 汇总
var seeds = document.createElement('details');
// seeds.textContent = 'Status';
seeds.className = 'seeds';
try {
if (Object.keys(seed).length > 0) {
seeds.innerHTML = `<summary>SEED</summary>
<div class="content"> </div>`
for (const id in seed) {
const s = seed[id];
let em = document.createElement('em');
em.innerText = s;
seeds.querySelector('.content').appendChild(em)
}
}
} catch (error) {
}
statusDiv.appendChild(status);
statusDiv.appendChild(seeds);
// 创建输入框
var input1 = createInputs(inputData)
var output = createOutputs(outputData)
var output = createOutputs(outputData, link)
// 创建提交按钮
var submitButton = document.createElement('button');
@@ -985,13 +1066,13 @@
leftDiv.appendChild(titleDiv);
leftDiv.appendChild(iconDes);
// leftDiv.appendChild(des);
leftDiv.appendChild(status);
leftDiv.appendChild(statusDiv);
leftDiv.appendChild(input1);
leftDiv.appendChild(submitButton);
mainDiv.appendChild(submitButton);
rightDiv.appendChild(output);
mainDiv.appendChild(leftDiv);
mainDiv.appendChild(leftDetails);
mainDiv.appendChild(rightDiv);
document.body.appendChild(mainDiv)
@@ -1122,9 +1203,9 @@
async function createApp(appData, share = true) {
console.log(appData)
// console.log(appData)
// 使用示例:
var ui = createUI(appData.input, appData.output, share);
var ui = createUI(appData.input, appData.output, appData.seed, share,appData.link);
// 更新标题
ui.title.update(appData.name || 'Mixlab APP');
+39 -10
View File
@@ -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,18 @@ 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
}
input[inputIds.indexOf(id)] = {
...data[id],
title: node.title,
@@ -96,14 +110,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') {
// seed 的类型收集
try {
seed[id] = node.widgets.filter(
w => w.name === 'seed'
)[0].linkedWidgets[0].value
} catch (error) {}
}
}
}
return { input, output }
return { input, output, seed }
}
function getUrl () {
@@ -147,6 +170,7 @@ async function save (json, download = false) {
const name = json[0],
version = json[5],
share_prefix = json[6], //用于分享的功能扩展
link=json[7],//用于创建界面上的跳转链接
description = json[4],
inputIds = json[2].split('\n').filter(f => f),
outputIds = json[3].split('\n').filter(f => f)
@@ -162,7 +186,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 +198,10 @@ async function save (json, download = false) {
version,
input,
output,
seed, //控制是fixed 还是random
share_prefix,
filename: `${name}_${version}_${new Date().toDateString()}.json`
link,
filename: `${name}_${version}.json`
}
try {
@@ -185,14 +211,18 @@ async function save (json, download = false) {
// let http_workflow = app.graph.serialize()
if (download) {
await save_app(data)
await downloadJsonFile(data, data.app.filename)
await save_app(data)
await downloadJsonFile(data, data.app.filename)
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
)}`
)
if (open)
window.open(
`${getUrl()}/mixlab/app?filename=${encodeURIComponent(data.app.filename)}`
`${getUrl()}/mixlab/app?filename=${encodeURIComponent(
data.app.filename
)}`
)
} else {
await save_app(data)
@@ -291,7 +321,6 @@ app.registerExtension({
nodeType.prototype.onExecuted = async function (message) {
onExecuted?.apply(this, arguments)
console.log(message.json)
window._mixlab_app_json = message.json
try {
const div = this.widgets.filter(w => w.div)[0].div
+1 -1
View File
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
const version = 'v0.7.0'
const version = 'v0.8.0'
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
.then(response => response.json())
+77 -1
View File
@@ -48,6 +48,38 @@ 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) {
let url = get_url()
const res = await fetch(`${url}/mixlab/workflow`, {
method: 'POST',
body: JSON.stringify({
task: 'my_app',
filename
})
})
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'
@@ -667,16 +699,32 @@ app.registerExtension({
...options
] // and return the options
}
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) // 考虑设备像素比
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)
}
},
async setup () {
// Add canvas menu options
const orig = LGraphCanvas.prototype.getCanvasMenuOptions
const apps = await get_my_app()
// console.log('apps',apps)
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
const options = orig.apply(this, arguments)
options.push(null, {
content: `Nodes Map ♾️Mixlab`,
disabled: false, // or a function determining whether to disable
disabled: false,
callback: async () => {
nodesMap =
nodesMap && Object.keys(nodesMap).length > 0
@@ -829,8 +877,36 @@ app.registerExtension({
if (!document.querySelector('#mixlab_find_the_node'))
document.body.appendChild(div)
}
},{
content: 'Workflow App ♾️Mixlab',
has_submenu: true,
disabled: false,
submenu: {
options: Array.from(apps, a => {
return {
content: a.name,
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) {}
}
}
})
}
})
// options.push({
// content: `Save For App ♾️Mixlab`,
// disabled: false, // or a function determining whether to disable
+34 -10
View File
@@ -2,7 +2,7 @@ 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 { addValueControlWidget } from "../../../scripts/widgets.js";
import { addValueControlWidget } from '../../../scripts/widgets.js'
const getLocalData = key => {
let data = {}
@@ -45,6 +45,21 @@ function get_position_style (ctx, widget_width, y, node_height) {
}
}
function hexToRGBA (hexColor) {
var hex = hexColor.replace('#', '')
var r = parseInt(hex.substring(0, 2), 16)
var g = parseInt(hex.substring(2, 4), 16)
var b = parseInt(hex.substring(4, 6), 16)
// 获取透明度的十六进制值
var alphaHex = hex.substring(6)
// 将透明度的十六进制值转换为十进制值
var alpha = parseInt(alphaHex, 16) / 255
return [r,g,b,alpha]
}
app.registerExtension({
name: 'Mixlab.utils.Color',
async getCustomWidgets (app) {
@@ -60,8 +75,16 @@ 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')
return data[node.id] || '#000000'
let data = getLocalData('_mixlab_utils_color');
let hex=data[node.id] || '#000000'
let [r,g,b,a]=hexToRGBA(hex)
return {
hex,
r,
g,
b,
a
}
}
}
// widget.something = something; // maybe adds stuff to it
@@ -109,7 +132,7 @@ app.registerExtension({
ip.style = `outline: none;
border: none;
padding: 4px;
width: 100%;cursor: pointer;
width: 70%;cursor: pointer;
height: 32px;`
const label = document.createElement('label')
label.style = 'font-size: 10px;min-width:32px'
@@ -164,16 +187,17 @@ app.registerExtension({
async beforeRegisterNodeDef (nodeType, nodeData, app) {
if (nodeType.comfyClass == 'TextToNumber') {
const onExecuted = nodeType.prototype.onExecuted
nodeType.prototype.onExecuted = function (message) {
onExecuted?.apply(this, arguments)
const random_number=this.widgets.filter(w=>w.name==='random_number')[0]
if(random_number.value==='enable'){
const n=this.widgets.filter(w=>w.name==='number')[0]
n.value=message.num[0]
const random_number = this.widgets.filter(
w => w.name === 'random_number'
)[0]
if (random_number.value === 'enable') {
const n = this.widgets.filter(w => w.name === 'number')[0]
n.value = message.num[0]
}
console.log('TextToNumber', random_number.value)
}
}
+723
View File
@@ -0,0 +1,723 @@
{
"last_node_id": 22,
"last_link_id": 23,
"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": 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": [
12
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"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": 11,
"type": "PreviewImage",
"pos": [
3319,
-161
],
"size": {
"0": 435.8727111816406,
"1": 511.8609619140625
},
"flags": {},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 13
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"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": 58
},
"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": [
-1
]
},
{
"id": 22,
"type": "PromptSlide",
"pos": [
2364,
-294
],
"size": {
"0": 315,
"1": 82
},
"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
]
},
{
"id": 12,
"type": "AppInfo",
"pos": [
2924,
846
],
"size": {
"0": 400,
"1": 272
},
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 12
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
13
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "AppInfo"
},
"widgets_values": [
"Prompt-weight-test",
"21\n22\n18",
"11\n16",
"",
1,
"",
null
]
},
{
"id": 21,
"type": "PromptSlide",
"pos": [
2360,
-544
],
"size": {
"0": 315,
"1": 82
},
"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
]
},
{
"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",
""
]
}
],
"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"
],
[
12,
10,
0,
12,
0,
"IMAGE"
],
[
13,
12,
0,
11,
0,
"IMAGE"
],
[
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"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
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