Compare commits

..
Author SHA1 Message Date
gold3bear 2918d4b07d correct Chinese text to prompt syntax 2024-01-14 11:50:14 +08:00
gold3bear c40e49be46 fix chinese prompt 2024-01-14 11:34:58 +08:00
shadowcz007 09957617d3 修复SwitchByIndex的bug 2024-01-13 20:59:29 +08:00
shadowcz007 c703aa7058 中文prompt增加选项,可控制是否添加更多 2024-01-13 20:59:07 +08:00
shadowcz007 64d366d323 Update prompt_mixlab.js 2024-01-13 17:12:03 +08:00
shadowcz007 333e0a2faa Update ImageNode.py 2024-01-13 16:32:51 +08:00
shadowcz007 a677d95bc8 Update README.md 2024-01-13 16:02:24 +08:00
shadowcz007 aafd87e84b v0.12.0 ChinesePrompt && PromptGenerate
> ChinesePrompt && PromptGenerate,中文prompt节点,直接用中文书写你的prompt

![](./assets/ChinesePrompt_workflow.svg)

> Web App增加图片编辑器
2024-01-13 15:59:52 +08:00
shadowcz007 efa3bae54b Create profession.txt 2024-01-13 12:25:05 +08:00
shadowcz007 8e4362689d Lama、ClipInterrogator安装移到节点内 2024-01-13 12:11:46 +08:00
shadowcz007 b86634284e appinfo运行bug 2024-01-13 11:34:17 +08:00
shadowcz007 b3293ddccd 优化promptImage的预览 2024-01-12 17:16:47 +08:00
shadowcz007 4b40831b83 prompt keywords 2024-01-11 23:05:25 +08:00
shadowcz007 e426b0521c 添加图片编辑功能 2024-01-11 23:01:58 +08:00
shadowcz007 1777bf6e06 修复切换workflow,数据未清空的情况 2024-01-11 23:01:42 +08:00
shadowcz007 dafc892f0f add image edit for web app 2024-01-11 15:41:05 +08:00
shadowcz007 fea0cfd5dd 更新workflow示例 2024-01-11 15:40:31 +08:00
shadowcz007 a7e158db6d update workflow example 2024-01-11 15:32:20 +08:00
shadowcz007 455ac4abd3 Update promptslide-appinfo-workflow.svg 2024-01-11 15:27:11 +08:00
shadowcz007 778dfa2cf5 update workflow example 2024-01-11 15:24:31 +08:00
shadowcz007 329f2e6f81 系统字体的获取 2024-01-10 16:19:49 +08:00
shadowcz007 63ad6d97d7 Update ImageNode.py 2024-01-09 22:59:34 +08:00
shadowcz007 8db56db7cf Update ImageNode.py 2024-01-09 22:34:13 +08:00
shadowcz007 bd542f1e0b Update app_mixlab.js 2024-01-09 18:42:36 +08:00
shadowcz007 a162e53dea Update ImageNode.py 2024-01-09 18:05:18 +08:00
shadowcz007 a8a4c848ed Update __init__.py 2024-01-09 16:15:15 +08:00
shadowcz007 bddd38996a Update ImageNode.py 2024-01-09 15:16:38 +08:00
shadowcz007 9f084eae94 修复LoadImagesFromPath的bug 2024-01-09 14:33:13 +08:00
shadowcz007 c54c635161 v0.11.4 2024-01-09 12:59:36 +08:00
shadowcz007 fa8b42e05e 增强ImageCrop功能 2024-01-09 12:48:20 +08:00
shadowcz007 9c3c323884 fixbug: user_manager 2024-01-09 12:41:29 +08:00
shadowcz007 c8a46439be v0.11.3 - 修复appinfo的logo输入 2024-01-09 09:17:00 +08:00
shadowcz007 b7ec701259 Update Utils.py 2024-01-09 09:16:15 +08:00
shadowcz007 a2cd0e0a38 v0.11.2
- 优化appinfo - 自动保存
- 优化mergeLayer,可以输出合成的mask
- 添加一个实验性的节点 ImageColorTransfer
- random prompt ,增加上传关键词功能
- 修复LoadImageFromPath的bug
2024-01-08 23:03:37 +08:00
shadowcz007 7bccc0e236 优化appinfo-不需要输出,每次运行都会自动更新数据 2024-01-08 23:01:31 +08:00
shadowcz007 a3a649a79f 修复 LoadImagesFromPath 不更新的bug 2024-01-08 22:11:10 +08:00
shadowcz007 2a14d30552 添加一个实验性的节点 ImageColorTransfer 2024-01-08 16:40:00 +08:00
shadowcz007 313bef0609 ClipInterrogator优化 2024-01-07 22:55:24 +08:00
shadowcz007 960a80aeca Merge branch 'main' of https://github.com/shadowcz007/comfyui-mixlab-nodes 2024-01-07 19:59:24 +08:00
shadowcz007 20e6d50a98 修复bug 2024-01-07 19:59:22 +08:00
shadow 507c3417d6 Merge pull request #114 from shadowcz007/fix_ssl_prot_occupied
fix:ssl prot occupied
2024-01-07 17:08:08 +08:00
34 changed files with 3204 additions and 980 deletions
+11 -17
View File
@@ -31,7 +31,7 @@ APP-JSON:
> 暂时支持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
@@ -67,7 +67,7 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
> PromptSlide
![](./assets/prompt_weight.png)
![](./workflow/promptslide-appinfo-workflow.svg)
<!-- ![](./workflow/promptslide-appinfo-workflow.svg) -->
> randomPrompt
@@ -79,6 +79,10 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
> PromptImage & PromptSimplification,Assist in simplifying prompt words, comparing images and prompt word nodes.
> ChinesePrompt && PromptGenerate,中文prompt节点,直接用中文书写你的prompt
![](./assets/ChinesePrompt_workflow.svg)
### 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.
@@ -159,23 +163,16 @@ 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 : models/clipseg
[Download lama](https://github.com/enesmsahin/simple-lama-inpainting/releases/download/v0.1.0/big-lama.pt), move to : models/lama
[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 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
@@ -216,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>
+48 -11
View File
@@ -503,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():
@@ -530,25 +535,26 @@ PromptServer.add_routes=new_add_routes
# 导入节点
from .nodes.PromptNode import RandomPrompt,PromptSlide,PromptSimplification,PromptImage
from .nodes.ImageNode import NoiseImage,TransparentImage,GradientImage,LoadImagesFromPath,LoadImagesFromURL,ResizeImage,TextImage,SvgImage,Image3D,ShowLayer,NewLayer,MergeLayers,AreaToMask,SmoothMask,FeatheredMask,SplitLongMask,ImageCropByAlpha,EnhanceImage,FaceToMask
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.ClipInterrogator import ClipInterrogator
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,
"ClipInterrogator":ClipInterrogator,
"MirroredImage":MirroredImage,
"NoiseImage":NoiseImage,
"GradientImage":GradientImage,
"TransparentImage":TransparentImage,
@@ -559,6 +565,7 @@ NODE_CLASS_MAPPINGS = {
"EnhanceImage":EnhanceImage,
"SvgImage":SvgImage,
"3DImage":Image3D,
"ImageColorTransfer":ImageColorTransfer,
"ShowLayer":ShowLayer,
"NewLayer":NewLayer,
"MergeLayers":MergeLayers,
@@ -590,7 +597,7 @@ NODE_CLASS_MAPPINGS = {
"GetImageSize_":GetImageSize_,
"SwitchByIndex":SwitchByIndex,
"LimitNumber":LimitNumber,
"LaMaInpainting":LaMaInpainting
# "LaMaInpainting":LaMaInpainting
# "GamePal":GamePal
}
@@ -612,14 +619,44 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"3DImage":"3DImage ♾️Mixlab",
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab",
"LaMaInpainting":"LaMaInpainting ♾️Mixlab",
"PromptSlide":"PromptSlide ♾️Mixlab"
# "GamePal":"GamePal ♾️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

+30
View File
@@ -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
+30
View File
@@ -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
+3
View File
@@ -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",
+101
View File
@@ -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
+3 -3
View File
@@ -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)
+121 -10
View File
@@ -1,15 +1,55 @@
import os
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 transformers import AutoProcessor, BlipForConditionalGeneration
from clip_interrogator import Config, Interrogator
# 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
@@ -28,11 +68,11 @@ def load_caption_model(model_path,config,t='blip-base'):
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))
@@ -58,7 +98,53 @@ def image_analysis_fn(ci,image):
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, artist_ranks, movement_ranks, trending_ranks, flavor_ranks
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
@@ -79,6 +165,10 @@ def image_to_prompt(ci,image, mode):
class ClipInterrogator:
global _available
available=_available
@classmethod
def INPUT_TYPES(s):
return {"required": {
@@ -86,17 +176,22 @@ class ClipInterrogator:
"prompt_mode": (['fast','classic','best','negative'],),
"image_analysis": (["off","on"],),
},
# "optional":{
# "output":("CLIPINTERROGATOR", {"multiline": True,"default": "", "dynamicPrompts": False})
# },
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("prompt",)
RETURN_TYPES = ("STRING","STRING",)
RETURN_NAMES = ("prompt","random_samples",)
FUNCTION = "run"
CATEGORY = "♾️Mixlab/prompt"
CATEGORY = "♾️Mixlab/Prompt"
OUTPUT_NODE = True
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
OUTPUT_IS_LIST = (True,True,)
global ci
ci = None
def run(self,image,prompt_mode,image_analysis):
@@ -156,5 +251,21 @@ class ClipInterrogator:
ci.caption_offloaded = True
# analysis_result=[]
# items = app.graph.getNodeById(31).widgets[2].value["items"]
random_samples=[]
return {"ui":{"prompt": prompt_result,"analysis":analysis_result},"result": (prompt_result,)}
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,)}
+4 -4
View File
@@ -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")
+299 -72
View File
@@ -10,7 +10,7 @@ import folder_paths
import json,io
from comfy.cli_args import args
import cv2
import math
from .Watcher import FolderWatcher
@@ -27,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.
@@ -203,6 +249,11 @@ def generate_gradient_image(width, height, start_color_hex, end_color_hex):
# 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):
@@ -344,7 +395,8 @@ def get_average_color_image(image):
im = Image.new("RGB", (image.width, image.height), (average_red, average_green, average_blue))
return im
hex=rgb_to_hex((average_red, average_green, average_blue))
return (im,hex)
@@ -516,7 +568,7 @@ def merge_images(bg_image, layer_image, mask, x, y, width, height, scale_option)
return bg_image
# 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
@@ -540,11 +592,11 @@ def resize_image(layer_image, scale_option, width, height,color="white"):
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 = int(original_width * scale)
new_height = int(original_height * scale)
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")
resized_image = resized_image.convert("RGB")
return resized_image
return layer_image
@@ -552,6 +604,8 @@ def resize_image(layer_image, scale_option, width, height,color="white"):
# 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)
@@ -724,7 +778,7 @@ class SmoothMask:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/mask"
CATEGORY = "♾️Mixlab/Mask"
INPUT_IS_LIST = False
@@ -772,7 +826,7 @@ class FeatheredMask:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/mask"
CATEGORY = "♾️Mixlab/Mask"
OUTPUT_IS_LIST = (False,)
@@ -839,7 +893,7 @@ class SplitLongMask:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/mask"
CATEGORY = "♾️Mixlab/Mask"
OUTPUT_IS_LIST = (True,)
@@ -883,7 +937,7 @@ class TransparentImage:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
# INPUT_IS_LIST = True, 一个batch传进来
OUTPUT_IS_LIST = (True,True,True,)
@@ -955,7 +1009,7 @@ class EnhanceImage:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
INPUT_IS_LIST = True
@@ -1021,7 +1075,7 @@ class LoadImagesFromPath:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
# INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,True,False,)
@@ -1047,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')
# 当开启了监听,则取最新的,第一个文件
@@ -1075,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
@@ -1086,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],)
@@ -1146,7 +1252,7 @@ class TextImage:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,False,)
@@ -1175,7 +1281,7 @@ class LoadImagesFromURL:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (True,True,)
@@ -1232,7 +1338,7 @@ class SvgImage:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,True,)
@@ -1266,7 +1372,7 @@ class Image3D:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,False,False,False,)
@@ -1306,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
@@ -1318,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,)
@@ -1353,7 +1465,7 @@ class FaceToMask:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/mask"
CATEGORY = "♾️Mixlab/Mask"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
@@ -1398,7 +1510,7 @@ class EmptyLayer:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/layer"
CATEGORY = "♾️Mixlab/Layer"
OUTPUT_IS_LIST = (True,)
@@ -1479,7 +1591,7 @@ class NewLayer:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/layer"
CATEGORY = "♾️Mixlab/Layer"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@@ -1567,7 +1679,7 @@ class ShowLayer:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/layer"
CATEGORY = "♾️Mixlab/Layer"
INPUT_IS_LIST = True
# OUTPUT_IS_LIST = (True,)
@@ -1606,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,)
@@ -1632,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']
@@ -1658,25 +1773,32 @@ 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):
@@ -1707,7 +1829,7 @@ class GradientImage:
FUNCTION = "run"
# 右键菜单目录
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
# 输入是否为列表
INPUT_IS_LIST = False
@@ -1787,7 +1909,7 @@ class NoiseImage:
FUNCTION = "run"
# 右键菜单目录
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
# 输入是否为列表
INPUT_IS_LIST = False
@@ -1857,15 +1979,15 @@ class ResizeImage:
}
}
RETURN_TYPES = ("IMAGE","IMAGE")
RETURN_NAMES = ("image","average_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 = (True,True,)
OUTPUT_IS_LIST = (True,True,True,)
def run(self,width,height,scale_option,image=None,average_color=['on'],fill_color=["#FFFFFF"]):
@@ -1877,28 +1999,133 @@ class ResizeImage:
imgs=[]
average_images=[]
hexs=[]
if image==None:
im=create_noisy_image(w,h,"RGB")
a_im=get_average_color_image(im)
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)
else:
for im in image:
im=tensor2pil(im)
im=resize_image(im,scale_option,w,h,fill_color)
im=im.convert('RGB')
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=get_average_color_image(im)
a_im,hex=get_average_color_image(im)
im=pil2tensor(im)
imgs.append(im)
im=pil2tensor(im)
imgs.append(im)
a_im=pil2tensor(a_im)
average_images.append(a_im)
a_im=pil2tensor(a_im)
average_images.append(a_im)
hexs.append(hex)
return (imgs,average_images,)
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,)
+40 -4
View File
@@ -1,12 +1,46 @@
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
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")
@@ -38,6 +72,8 @@ def pil2tensor(image):
class LaMaInpainting:
global _available
available=_available
@classmethod
def INPUT_TYPES(s):
return {"required": {
@@ -52,7 +88,7 @@ class LaMaInpainting:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
CATEGORY = "♾️Mixlab/Image"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
+4 -4
View File
@@ -114,7 +114,7 @@ class PromptImage:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/prompt"
CATEGORY = "♾️Mixlab/Prompt"
# 运行的函数
def run(self,prompts,images,save_to_image):
@@ -193,7 +193,7 @@ class PromptSimplification:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/prompt"
CATEGORY = "♾️Mixlab/Prompt"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@@ -254,7 +254,7 @@ class PromptSlide:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/prompt"
CATEGORY = "♾️Mixlab/Prompt"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
@@ -306,7 +306,7 @@ class RandomPrompt:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/prompt"
CATEGORY = "♾️Mixlab/Prompt"
OUTPUT_IS_LIST = (True,)
OUTPUT_NODE = True
+2 -2
View File
@@ -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,)
+271
View File
@@ -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,)}
+144 -51
View File
@@ -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
@@ -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}")
@@ -85,7 +131,12 @@ def flatten_list(nested_list):
if isinstance(item, list):
flat_list.extend(flatten_list(item))
else:
flat_list.append(item)
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
@@ -103,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,)
@@ -132,7 +183,7 @@ class FontInput:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
CATEGORY = "♾️Mixlab/Utils"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
@@ -162,7 +213,7 @@ class TextToNumber:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
CATEGORY = "♾️Mixlab/Utils"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
@@ -220,7 +271,7 @@ class FloatSlider:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
CATEGORY = "♾️Mixlab/Utils"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
@@ -273,7 +324,7 @@ class IntNumber:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
CATEGORY = "♾️Mixlab/Utils"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
@@ -304,7 +355,7 @@ class MultiplicationNode:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
CATEGORY = "♾️Mixlab/Utils"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,False,)
@@ -326,7 +377,7 @@ class TextInput:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
CATEGORY = "♾️Mixlab/Utils"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
@@ -400,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 )
# 获取开始时间戳
@@ -434,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,
@@ -451,12 +502,13 @@ class AppInfo:
"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"
@@ -464,11 +516,16 @@ class AppInfo:
OUTPUT_NODE = True
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
# OUTPUT_IS_LIST = (True,)
def run(self,name,image,input_ids,output_ids,description,version,share_prefix,link,category):
def run(self,name,input_ids,output_ids,IMAGE,description,version,share_prefix,link,category,auto_save):
name=name[0]
im=image[0][0]
im=None
if IMAGE:
im=IMAGE[0][0]
#TODO batch 的方式需要处理
im=create_temp_file(im)
# image [img,] img[batch,w,h,a] 列表里面是batch,
input_ids=input_ids[0]
@@ -478,38 +535,14 @@ class AppInfo:
share_prefix=share_prefix[0]
link=link[0]
category=category[0]
#TODO batch 的方式需要处理
im=create_temp_file(im)
# 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,link,category]}, "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
@@ -534,7 +567,7 @@ class SwitchByIndex:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
CATEGORY = "♾️Mixlab/Utils"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@@ -545,7 +578,8 @@ class SwitchByIndex:
C=[]
index=index[0]
for a in A:
for a in A:
C.append(a)
for b in B:
C.append(b)
@@ -558,6 +592,7 @@ class SwitchByIndex:
C=[C[index]]
except Exception as e:
C=[]
return (C,)
@@ -590,7 +625,7 @@ class LimitNumber:
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
CATEGORY = "♾️Mixlab/Utils"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
@@ -612,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
View File
@@ -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 -2
View File
@@ -4,5 +4,5 @@ watchdog
opencv-python-headless
matplotlib
openai
simple-lama-inpainting
clip-interrogator==0.6.0
# simple-lama-inpainting
# clip-interrogator==0.6.0
+242 -8
View File
@@ -303,10 +303,9 @@
}
select,
button,
input {
.card select,
.card button,
.card input {
height: 32px;
cursor: pointer;
background: #000000bf;
@@ -332,14 +331,53 @@
font-size: 12px;
width: 200px;
}
/* 图像编辑器 */
#editor_container {
position: fixed;
top: 0;
z-index: 9;
background: #eee;
height: 100vh;
width: 100%;
display: none;
}
/* 图片的字幕 */
.pswp__custom-caption {
background: rgb(20 27 70);
font-size: 16px;
color: #fff;
width: calc(100% - 32px);
max-width: 400px;
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;
}
</style>
<!-- <script src="../../../scripts/api.js" type="module"></script> -->
<link href="/extensions/comfyui-mixlab-nodes/lib/photoswipe.min.css" rel="stylesheet">
<link href="/extensions/comfyui-mixlab-nodes/lib/classic.min.css" rel="stylesheet">
<script src="/extensions/comfyui-mixlab-nodes/lib/pickr.min.js"></script>
<script src="/extensions/comfyui-mixlab-nodes/lib/filerobot-image-editor.min.js"></script>
</head>
<body>
<div id="editor_container"></div>
<div style="display: flex;
align-items: center;
justify-content: space-around;">
@@ -363,8 +401,8 @@
</svg> Code
</a>
</div>
<!-- <script type="module" src="https://cdn.bootcdn.net/ajax/libs/photoswipe/5.4.0/photoswipe-lightbox.esm.min.js"></script> -->
<script type="module">
import PhotoSwipeLightbox from '/extensions/comfyui-mixlab-nodes/lib/photoswipe-lightbox.esm.min.js'
@@ -406,8 +444,164 @@
return { url: src, name }
};
function base64ToBlob(base64) {
// 去除base64编码中的前缀
const base64WithoutPrefix = base64.replace(/^data:image\/\w+;base64,/, '');
// 将base64编码转换为字节数组
const byteCharacters = atob(base64WithoutPrefix);
// 创建一个存储字节数组的数组
const byteArrays = [];
// 将字节数组放入数组中
for (let offset = 0; offset < byteCharacters.length; offset += 1024) {
const slice = byteCharacters.slice(offset, offset + 1024);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
// 创建blob对象
const blob = new Blob(byteArrays, { type: 'image/png' }); // 根据实际情况设置MIME类型
return blob;
}
function editImage(img, data) {
const update = async () => {
const { imageData } = filerobotImageEditor.getCurrentImgData()
let base64 = imageData.imageBase64;
let fileBlob = base64ToBlob(base64)
// // 获取读取的文件内容,即 Blob 对象
let hashId = await calculateImageHash(fileBlob)
if (hashId == window._appData.data[data.id].hashId) return
let { url, name } = await uploadImage(fileBlob);
// 在这里可以对 Blob 对象进行进一步处理
// imageElement.src = url;
window._appData.data[data.id].inputs.image = name;
window._appData.data[data.id].hashId = hashId;
console.log("上传的文件:", url, data.id, name);
img.src = base64;
}
const { TABS, TOOLS } = FilerobotImageEditor;
const config = {
source: img.src,
// loadableDesignState:{ //默认值
// annotations:{
// watermark:{
// image:'https://127.0.0.1:8189/view?filename=1703554480406.png&type=input&subfolder=&rand=0.044164708320141965',
// width:100,
// height:200,
// x:10,
// y:50,
// name: "Image",
// id:'watermark'
// }
// }
// },
// annotationsCommon: {
// fill: '#ff0000',
// },
// Text: { text: 'Filerobot...' },
Rotate: { angle: 90, componentType: 'slider' },
Crop: {
presetsItems: [
{
titleKey: 'classicTv',
descriptionKey: '4:3',
ratio: 4 / 3,
// icon: CropClassicTv,
},
{
titleKey: 'cinemascope',
descriptionKey: '21:9',
ratio: 21 / 9,
// icon: CropCinemaScope,
},
],
presetsFolders: [
{
titleKey: 'socialMedia', // will be translated into Social Media as backend contains this translation key
// icon: Social, // optional,
groups: [
{
titleKey: 'facebook',
items: [
{
titleKey: 'profile',
width: 180,
height: 180,
descriptionKey: 'fbProfileSize',
},
{
titleKey: 'coverPhoto',
width: 820,
height: 312,
descriptionKey: 'fbCoverPhotoSize',
},
],
},
],
},
],
},
tabsIds: [...Object.values(TABS)], // or ['Adjust', 'Annotate', 'Watermark']
defaultTabId: TABS.WATERMARK, // or 'Annotate'
defaultToolId: TOOLS.WATERMARK, // or 'Text'
closeAfterSave: true
};
let editor = document.querySelector('#editor_container')
// Assuming we have a div with id="editor_container"
editor.style.display = 'block';
// console.log(img,editor,data)
document.body.style.overflow = 'hidden'
const filerobotImageEditor = new FilerobotImageEditor(
editor,
config,
);
filerobotImageEditor.render({
onSave: (editedImageObject, designState) => {
console.log('saved', designState)
//adjustments
update()
},
onClose: (closingReason) => {
console.log('Closing reason', closingReason);
filerobotImageEditor.terminate();
editor.style.display = 'none'
document.body.style.overflow = 'auto'
},
});
}
async function getQueue(clientId) {
try {
@@ -892,6 +1086,12 @@
btnFromClipboard.innerText = 'paste from clipboard'
actionDiv.appendChild(btnFromClipboard);
const btnForImageEdit = document.createElement("button");
btnForImageEdit.style = ` width: 32px; background: none;margin-left: 18px;`
btnForImageEdit.innerHTML = '<?xml version="1.0" ?><svg version="1.1" style="width: 24px;" 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>'
actionDiv.appendChild(btnForImageEdit);
uploadContainer.appendChild(actionDiv)
// Create an image element to display the uploaded image
@@ -902,6 +1102,8 @@
btnFromClipboard.addEventListener('click', (event) => handleClipboardImage(imageElement, data));
btnForImageEdit.addEventListener('click', e => editImage(imageElement, data))
uploadImageInput.addEventListener('click', (event) => {
uploadImageInputHide.click()
@@ -1624,6 +1826,7 @@
p.className = 'prompt_image'
p.innerText = prompt;
a.appendChild(p)
img.alt=prompt
}
// imgDiv.parentElement.appendChild(a);
@@ -1931,6 +2134,36 @@
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();
// window._lightbox = lightbox
@@ -2006,13 +2239,14 @@
createApp(window._appData);
};
init_app()
</script>
init_app();
</script>
</body>
</html>
+102 -18
View File
@@ -100,16 +100,17 @@ function extractInputAndOutputData (jsonData, inputIds = [], outputIds = []) {
// min max step
options = node.widgets.filter(w => w.type === 'slider')[0].options
// 备选的keywords清单
let ks = getLocalData(`_mixlab_PromptSlide`)
let keywords = ks[id]
// console.log('keywords',keywords)
if (keywords && keywords[0]) {
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'){
if (node.type == 'Color') {
}
input[inputIds.indexOf(id)] = {
@@ -126,11 +127,11 @@ function extractInputAndOutputData (jsonData, inputIds = [], outputIds = []) {
output[outputIds.indexOf(id)] = { ...data[id], title: node.title, id }
}
if (node.type === 'KSampler'||node.type=='SamplerCustom') {
if (node.type === 'KSampler' || node.type == 'SamplerCustom') {
// seed 的类型收集
try {
seed[id] = node.widgets.filter(
w => (w.name === 'seed'||w.name=='noise_seed')
w => w.name === 'seed' || w.name == 'noise_seed'
)[0].linkedWidgets[0].value
} catch (error) {}
}
@@ -188,7 +189,8 @@ 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], //用于分享的功能扩展
@@ -237,22 +239,53 @@ async function save (json, download = false) {
if (download) {
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
)}&category=${encodeURIComponent(data.app.category)}`
)
if (open)
window.open(
`${getUrl()}/mixlab/app?filename=${encodeURIComponent(
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
)}&category=${encodeURIComponent(data.app.category)}`
)
if (open)
window.open(
`${getUrl()}/mixlab/app?filename=${encodeURIComponent(
data.app.filename
)}&category=${encodeURIComponent(data.app.category)}`
)
}
} catch (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
}
}
app.registerExtension({
name: 'Mixlab.utils.AppInfo',
async beforeRegisterNodeDef (nodeType, nodeData, app) {
@@ -260,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',
@@ -331,6 +373,8 @@ app.registerExtension({
}
this.serialize_widgets = true //需要保存参数
window._mixlab_app_json = null
}
const onExecuted = nodeType.prototype.onExecuted
@@ -339,6 +383,12 @@ app.registerExtension({
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'),
@@ -347,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)
}
}
}
})
+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.11.0'
const version = 'v0.12.0'
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
.then(response => response.json())
+79 -25
View File
@@ -3,11 +3,57 @@ import { app } from '../../../scripts/app.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) {
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?.()
@@ -15,29 +61,9 @@ app.registerExtension({
this.widgets.length = 2
}
const w = ComfyWidgets['STRING'](
this,
'text',
['STRING', { multiline: true }],
app
).widget
w.inputEl.readOnly = true
w.inputEl.style.opacity = 0.6
createPrompt(this, prompts, items, random_samples)
w.value = prompts.join('\n\n')
const w2 = ComfyWidgets['STRING'](
this,
'text',
['STRING', { multiline: true }],
app
).widget
w2.inputEl.readOnly = true
w2.inputEl.style.opacity = 0.6
w2.value = JSON.stringify(items, null, 2)
console.log('ClipInterrogator',w,w2)
// console.log('ClipInterrogator', w, w2)
requestAnimationFrame(() => {
const sz = this.computeSize()
if (sz[0] < this.size[0]) {
@@ -55,11 +81,39 @@ app.registerExtension({
const onExecuted = nodeType.prototype.onExecuted
nodeType.prototype.onExecuted = function (message) {
onExecuted?.apply(this, arguments)
console.log('##', message)
populate.call(this, message.prompt, message.analysis)
// 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) {}
}
}
})
+99 -4
View File
@@ -3,6 +3,87 @@ 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
@@ -381,11 +462,19 @@ const _createResult = async (node, widget, message) => {
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 = `<img src="${url}" style='width: 100%'/>
<p style="position: absolute;
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;
background:#444444c2;
left: 0;
opacity: 0.6;
background-color: var(--comfy-input-bg);
color: var(--descrip-text);
margin: 0;
font-size: 12px;
padding: 5px;
@@ -422,11 +511,14 @@ app.registerExtension({
}
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()
@@ -472,6 +564,9 @@ app.registerExtension({
// 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]
+37 -9
View File
@@ -269,7 +269,6 @@ app.registerExtension({
const n = this.widgets.filter(w => w.name === 'number')[0]
n.value = message.num[0]
}
console.log('TextToNumber', random_number.value)
}
}
@@ -307,11 +306,16 @@ const min_max = node => {
app.registerExtension({
name: 'Mixlab.utils.FloatSlider',
async beforeRegisterNodeDef (nodeType, nodeData, app) {
const orig_nodeCreated = nodeType.prototype.onNodeCreated
nodeType.prototype.onNodeCreated = function () {
orig_nodeCreated?.apply(this, arguments)
min_max(this)
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') {
@@ -322,11 +326,15 @@ app.registerExtension({
app.registerExtension({
name: 'Mixlab.utils.IntNumber',
async beforeRegisterNodeDef (nodeType, nodeData, app) {
const orig_nodeCreated = nodeType.prototype.onNodeCreated
nodeType.prototype.onNodeCreated = function () {
orig_nodeCreated?.apply(this, arguments)
min_max(this)
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') {
@@ -334,3 +342,23 @@ app.registerExtension({
}
}
})
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)
};
}
},
})
+59 -30
View File
@@ -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) {}
}
}
})
File diff suppressed because one or more lines are too long
+736
View File
@@ -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
}
+57 -57
View File
@@ -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,
@@ -1,6 +1,6 @@
{
"last_node_id": 128,
"last_link_id": 207,
"last_node_id": 129,
"last_link_id": 208,
"nodes": [
{
"id": 8,
@@ -466,7 +466,7 @@
"1": 262
},
"flags": {},
"order": 28,
"order": 27,
"mode": 0,
"inputs": [
{
@@ -526,7 +526,7 @@
"1": 262
},
"flags": {},
"order": 30,
"order": 29,
"mode": 0,
"inputs": [
{
@@ -574,31 +574,6 @@
""
]
},
{
"id": 101,
"type": "PreviewImage",
"pos": [
5328.397364750004,
-1726.0537115390628
],
"size": {
"0": 210,
"1": 246
},
"flags": {},
"order": 27,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 161
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 21,
"type": "Note",
@@ -670,7 +645,7 @@
"1": 262
},
"flags": {},
"order": 32,
"order": 31,
"mode": 0,
"inputs": [
{
@@ -1148,7 +1123,7 @@
"1": 46
},
"flags": {},
"order": 34,
"order": 33,
"mode": 0,
"inputs": [
{
@@ -1171,6 +1146,12 @@
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
@@ -1189,7 +1170,7 @@
"1": 46
},
"flags": {},
"order": 35,
"order": 34,
"mode": 0,
"inputs": [
{
@@ -1212,6 +1193,12 @@
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
@@ -1230,7 +1217,7 @@
"1": 46
},
"flags": {},
"order": 36,
"order": 35,
"mode": 0,
"inputs": [
{
@@ -1253,6 +1240,12 @@
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
@@ -1304,7 +1297,7 @@
"1": 532.8897705078125
},
"flags": {},
"order": 37,
"order": 36,
"mode": 0,
"inputs": [
{
@@ -1329,7 +1322,7 @@
"1": 553.1112670898438
},
"flags": {},
"order": 38,
"order": 37,
"mode": 0,
"inputs": [
{
@@ -1354,7 +1347,7 @@
"1": 150
},
"flags": {},
"order": 31,
"order": 30,
"mode": 0,
"inputs": [
{
@@ -1464,7 +1457,7 @@
"1": 499.6607971191406
},
"flags": {},
"order": 39,
"order": 38,
"mode": 0,
"inputs": [
{
@@ -1489,7 +1482,7 @@
"1": 150
},
"flags": {},
"order": 33,
"order": 32,
"mode": 0,
"inputs": [
{
@@ -1554,7 +1547,7 @@
"1": 150
},
"flags": {},
"order": 29,
"order": 28,
"mode": 0,
"inputs": [
{
@@ -1705,56 +1698,10 @@
101,
0,
"#000000",
false,
false
]
},
{
"id": 98,
"type": "AppInfo",
"pos": [
4922.397364750004,
-1807.0537115390628
],
"size": {
"0": 362.42608642578125,
"1": 431.3537902832031
},
"flags": {},
"order": 16,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 162
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
161
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "AppInfo"
},
"widgets_values": [
"AIPC大赛模板",
"\n75\n118",
"57\n126\n127\n",
"投稿模板#发挥你的想象力",
4,
"#AIPC# 参赛作品",
"https://www.weibo.com",
"",
null
]
},
{
"id": 106,
"type": "LoadImage",
@@ -1762,10 +1709,10 @@
4535,
-1843
],
"size": [
210,
314
],
"size": {
"0": 210,
"1": 314
},
"flags": {},
"order": 7,
"mode": 0,
@@ -1774,7 +1721,7 @@
"name": "IMAGE",
"type": "IMAGE",
"links": [
162
208
],
"shape": 3,
"slot_index": 0
@@ -1835,6 +1782,43 @@
"1703665898041.png",
"image"
]
},
{
"id": 129,
"type": "AppInfo",
"pos": [
4893,
-1778
],
"size": {
"0": 400,
"1": 344.0000305175781
},
"flags": {},
"order": 16,
"mode": 0,
"inputs": [
{
"name": "LOGO",
"type": "IMAGE",
"link": 208
}
],
"properties": {
"Node name for S&R": "AppInfo"
},
"widgets_values": [
"AIPC大赛模板",
"\n75\n118",
"57\n126\n127\n",
"投稿模板#发挥你的想象力",
4,
"#AIPC# 参赛作品",
"https://www.weibo.com",
"",
"enable",
1
]
}
],
"links": [
@@ -2174,22 +2158,6 @@
0,
"IMAGE"
],
[
161,
98,
0,
101,
0,
"IMAGE"
],
[
162,
106,
0,
98,
0,
"IMAGE"
],
[
185,
118,
@@ -2333,6 +2301,14 @@
127,
0,
"IMAGE"
],
[
208,
106,
0,
129,
0,
"IMAGE"
]
],
"groups": [
@@ -2351,10 +2327,10 @@
{
"title": "Group",
"bounding": [
4912,
4883,
-2035,
637,
669
666,
611
],
"color": "#444",
"font_size": 24,
+133 -135
View File
@@ -1,6 +1,6 @@
{
"last_node_id": 22,
"last_link_id": 23,
"last_node_id": 23,
"last_link_id": 25,
"nodes": [
{
"id": 9,
@@ -167,48 +167,6 @@
"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",
@@ -248,31 +206,6 @@
"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",
@@ -349,7 +282,7 @@
],
"size": {
"0": 315,
"1": 58
"1": 130
},
"flags": {},
"order": 2,
@@ -370,7 +303,10 @@
"Node name for S&R": "IntNumber"
},
"widgets_values": [
-1
0,
0,
1,
1
]
},
{
@@ -382,7 +318,7 @@
],
"size": {
"0": 315,
"1": 82
"1": 106
},
"flags": {},
"order": 3,
@@ -403,51 +339,7 @@
},
"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,
"",
1.26,
null
]
},
@@ -460,7 +352,7 @@
],
"size": {
"0": 315,
"1": 82
"1": 106
},
"flags": {},
"order": 4,
@@ -481,7 +373,8 @@
},
"widgets_values": [
"Black and White",
1.26
1.26,
null
]
},
{
@@ -584,6 +477,111 @@
"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": [
@@ -651,22 +649,6 @@
1,
"VAE"
],
[
12,
10,
0,
12,
0,
"IMAGE"
],
[
13,
12,
0,
11,
0,
"IMAGE"
],
[
16,
15,
@@ -714,6 +696,22 @@
13,
1,
"STRING"
],
[
24,
10,
0,
11,
0,
"IMAGE"
],
[
25,
10,
0,
23,
0,
"IMAGE"
]
],
"groups": [],
+166 -164
View File
@@ -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

+281 -235
View File
@@ -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": [],
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 589 B