Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
812879610a | ||
|
|
c5e521ccc1 | ||
|
|
bc1c8fa351 | ||
|
|
7271fcf9c1 | ||
|
|
ace3b7707b | ||
|
|
9eb65cc4ee | ||
|
|
c5c2bc779c | ||
|
|
ae1751d9c0 | ||
|
|
d17583ef7d | ||
|
|
a363713ae0 | ||
|
|
1566165bd4 | ||
|
|
fe065fa318 | ||
|
|
202d5cf071 | ||
|
|
b785a9dc5b | ||
|
|
73bc658b2f | ||
|
|
0b94216138 | ||
|
|
86eec2b4cc | ||
|
|
c99b531d28 | ||
|
|
74a4338cb5 | ||
|
|
d691c52e49 | ||
|
|
bd3e9e4b3c | ||
|
|
3c3ca5fb9c | ||
|
|
e4ff4fce1c | ||
|
|
2918d4b07d | ||
|
|
c40e49be46 | ||
|
|
4ccda20975 | ||
|
|
09957617d3 | ||
|
|
c703aa7058 | ||
|
|
64d366d323 | ||
|
|
333e0a2faa | ||
|
|
a677d95bc8 |
@@ -67,7 +67,7 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
> PromptSlide
|
||||

|
||||
|
||||

|
||||
<!--  -->
|
||||
|
||||
> randomPrompt
|
||||
|
||||
@@ -164,13 +164,16 @@ An improvement has been made to directly redirect to GitHub to search for missin
|
||||
|
||||
|
||||
### Models
|
||||
|
||||
[Download rembg Models](https://github.com/danielgatis/rembg/tree/main#Models),move to:models/rembg
|
||||
|
||||
[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 succinctly/text2image-prompt-generator](https://huggingface.co/succinctly/text2image-prompt-generator/tree/main),move to:text_generator/text2image-prompt-generator
|
||||
[Download succinctly/text2image-prompt-generator](https://huggingface.co/succinctly/text2image-prompt-generator/tree/main),move to:prompt_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
|
||||
|
||||
|
||||
+41
-12
@@ -192,7 +192,7 @@ def read_workflow_json_files(folder_path ):
|
||||
def get_workflows():
|
||||
# print("#####path::", current_path)
|
||||
workflow_path=os.path.join(current_path, "workflow")
|
||||
print('workflow_path: ',workflow_path)
|
||||
# print('workflow_path: ',workflow_path)
|
||||
if not os.path.exists(workflow_path):
|
||||
# 使用mkdir()方法创建新目录
|
||||
os.mkdir(workflow_path)
|
||||
@@ -231,8 +231,14 @@ def get_my_workflow_for_app(filename="my_workflow_app.json",category="",is_all=F
|
||||
})
|
||||
else:
|
||||
category=''
|
||||
input=None
|
||||
output=None
|
||||
if 'category' in x['app']:
|
||||
category=x['app']['category']
|
||||
if 'input' in x['app']:
|
||||
input=x['app']['input']
|
||||
if 'output' in x['app']:
|
||||
output=x['app']['output']
|
||||
apps.append({
|
||||
"filename":item["filename"],
|
||||
"category":category,
|
||||
@@ -244,6 +250,8 @@ def get_my_workflow_for_app(filename="my_workflow_app.json",category="",is_all=F
|
||||
"icon":(x['app']['icon'] if 'icon' in x['app'] else None),
|
||||
"name":x['app']['name'],
|
||||
"version":x['app']['version'],
|
||||
"input":input,
|
||||
"output":output
|
||||
}
|
||||
},
|
||||
"date":item["date"]
|
||||
@@ -271,8 +279,14 @@ def get_my_workflow_for_app(filename="my_workflow_app.json",category="",is_all=F
|
||||
# print(apps[0]['filename'] ,item["filename"])
|
||||
if apps[0]['filename']!=item["filename"]:
|
||||
category=''
|
||||
input=None
|
||||
output=None
|
||||
if 'category' in x['app']:
|
||||
category=x['app']['category']
|
||||
if 'input' in x['app']:
|
||||
input=x['app']['input']
|
||||
if 'output' in x['app']:
|
||||
output=x['app']['output']
|
||||
apps.append({
|
||||
"filename":item["filename"],
|
||||
# "category":category,
|
||||
@@ -284,6 +298,8 @@ def get_my_workflow_for_app(filename="my_workflow_app.json",category="",is_all=F
|
||||
"icon":(x['app']['icon'] if 'icon' in x['app'] else None),
|
||||
"name":x['app']['name'],
|
||||
"version":x['app']['version'],
|
||||
"input":input,
|
||||
"output":output
|
||||
}
|
||||
},
|
||||
"date":item["date"]
|
||||
@@ -534,15 +550,15 @@ PromptServer.add_routes=new_add_routes
|
||||
|
||||
|
||||
# 导入节点
|
||||
from .nodes.PromptNode import RandomPrompt,PromptSlide,PromptSimplification,PromptImage
|
||||
from .nodes.ImageNode import GetImageSize_,MirroredImage,ImageColorTransfer,NoiseImage,TransparentImage,GradientImage,LoadImagesFromPath,LoadImagesFromURL,ResizeImage,TextImage,SvgImage,Image3D,ShowLayer,NewLayer,MergeLayers,AreaToMask,SmoothMask,FeatheredMask,SplitLongMask,ImageCropByAlpha,EnhanceImage,FaceToMask
|
||||
from .nodes.PromptNode import EmbeddingPrompt,RandomPrompt,PromptSlide,PromptSimplification,PromptImage,JoinWithDelimiter
|
||||
from .nodes.ImageNode import SplitImage,GridOutput,GetImageSize_,MirroredImage,ImageColorTransfer,NoiseImage,TransparentImage,GradientImage,LoadImagesFromPath,LoadImagesFromURL,ResizeImage,TextImage,SvgImage,Image3D,ShowLayer,NewLayer,MergeLayers,AreaToMask,SmoothMask,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 TESTNODE_,AppInfo,IntNumber,FloatSlider,TextInput,ColorInput,FontInput,TextToNumber,DynamicDelayProcessor,LimitNumber,SwitchByIndex,MultiplicationNode
|
||||
|
||||
from .nodes.Mask import OutlineMask,FeatheredMask
|
||||
|
||||
|
||||
# 要导出的所有节点及其名称的字典
|
||||
@@ -551,6 +567,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"AppInfo":AppInfo,
|
||||
"TESTNODE_":TESTNODE_,
|
||||
"RandomPrompt":RandomPrompt,
|
||||
"EmbeddingPrompt":EmbeddingPrompt,
|
||||
"PromptSlide":PromptSlide,
|
||||
"PromptSimplification":PromptSimplification,
|
||||
"PromptImage":PromptImage,
|
||||
@@ -568,6 +585,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ImageColorTransfer":ImageColorTransfer,
|
||||
"ShowLayer":ShowLayer,
|
||||
"NewLayer":NewLayer,
|
||||
"SplitImage":SplitImage,
|
||||
"GridOutput":GridOutput,
|
||||
"MergeLayers":MergeLayers,
|
||||
"SplitLongMask":SplitLongMask,
|
||||
"FeatheredMask":FeatheredMask,
|
||||
@@ -597,6 +616,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
"GetImageSize_":GetImageSize_,
|
||||
"SwitchByIndex":SwitchByIndex,
|
||||
"LimitNumber":LimitNumber,
|
||||
"OutlineMask":OutlineMask,
|
||||
"JoinWithDelimiter":JoinWithDelimiter
|
||||
# "LaMaInpainting":LaMaInpainting
|
||||
# "GamePal":GamePal
|
||||
}
|
||||
@@ -622,7 +643,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"PromptSlide":"PromptSlide ♾️Mixlab",
|
||||
"PromptGenerate_Mix":"PromptGenerate ♾️Mixlab",
|
||||
"ChinesePrompt_Mix":"ChinesePrompt ♾️Mixlab",
|
||||
"GamePal":"GamePal ♾️Mixlab"
|
||||
"GamePal":"GamePal ♾️Mixlab",
|
||||
"RembgNode_Mix":"Removebg"
|
||||
}
|
||||
|
||||
# web ui的节点功能
|
||||
@@ -636,16 +658,16 @@ try:
|
||||
print('LaMaInpainting.available',LaMaInpainting.available)
|
||||
if LaMaInpainting.available:
|
||||
NODE_CLASS_MAPPINGS['LaMaInpainting']=LaMaInpainting
|
||||
except:
|
||||
print('LaMaInpainting.available',False)
|
||||
except Exception as e:
|
||||
print('LaMaInpainting.available',False,e)
|
||||
|
||||
try:
|
||||
from .nodes.ClipInterrogator import ClipInterrogator
|
||||
print('ClipInterrogator.available',ClipInterrogator.available)
|
||||
if ClipInterrogator.available:
|
||||
NODE_CLASS_MAPPINGS['ClipInterrogator']=ClipInterrogator
|
||||
except:
|
||||
print('ClipInterrogator.available',False)
|
||||
except Exception as e:
|
||||
print('ClipInterrogator.available',False,e)
|
||||
|
||||
try:
|
||||
from .nodes.TextGenerateNode import PromptGenerate,ChinesePrompt
|
||||
@@ -655,8 +677,15 @@ try:
|
||||
print('ChinesePrompt.available',ChinesePrompt.available)
|
||||
if ChinesePrompt.available:
|
||||
NODE_CLASS_MAPPINGS['ChinesePrompt_Mix']=ChinesePrompt
|
||||
except:
|
||||
print('TextGenerateNode.available',False)
|
||||
|
||||
except Exception as e:
|
||||
print('TextGenerateNode.available',False,e)
|
||||
|
||||
try:
|
||||
from .nodes.RembgNode import RembgNode_
|
||||
print('RembgNode_.available',RembgNode_.available)
|
||||
if RembgNode_.available:
|
||||
NODE_CLASS_MAPPINGS['RembgNode_Mix']=RembgNode_
|
||||
except Exception as e:
|
||||
print('RembgNode_.available',False,e)
|
||||
|
||||
print('\033[93m -------------- \033[0m')
|
||||
@@ -4761,7 +4761,10 @@
|
||||
],
|
||||
"https://github.com/shadowcz007/comfyui-mixlab-nodes": [
|
||||
[
|
||||
"GridOutput",
|
||||
"SplitImage",
|
||||
"PromptGenerate_Mix",
|
||||
"JoinWithDelimiter",
|
||||
"ChinesePrompt_Mix",
|
||||
"3DImage",
|
||||
"AppInfo",
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
Residential space
|
||||
Apartment building
|
||||
Villa
|
||||
Bungalow
|
||||
Condominium
|
||||
Commercial space
|
||||
Shopping mall
|
||||
Supermarket
|
||||
Restaurant
|
||||
Store
|
||||
Market
|
||||
Office space
|
||||
Office building
|
||||
Office
|
||||
Meeting room
|
||||
Co-working space
|
||||
Educational space
|
||||
School
|
||||
University
|
||||
Training institution
|
||||
Library
|
||||
Laboratory
|
||||
Medical space
|
||||
Hospital
|
||||
Clinic
|
||||
Pharmacy
|
||||
Nursing home
|
||||
Rehabilitation center
|
||||
Cultural space
|
||||
Museum
|
||||
Library
|
||||
Theater
|
||||
Concert hall
|
||||
Gallery
|
||||
Sports space
|
||||
Sports stadium
|
||||
Gym
|
||||
Swimming pool
|
||||
Basketball court
|
||||
Football field
|
||||
Transportation space
|
||||
Airport
|
||||
Train station
|
||||
Subway station
|
||||
Bus stop
|
||||
Parking lot
|
||||
Public space
|
||||
Park
|
||||
Square
|
||||
Street
|
||||
Pedestrian street
|
||||
Community center
|
||||
Industrial space
|
||||
Factory
|
||||
Warehouse
|
||||
Production workshop
|
||||
Mine
|
||||
Power plant
|
||||
@@ -24,6 +24,7 @@ def is_installed(package):
|
||||
return False
|
||||
return spec is not None
|
||||
|
||||
|
||||
try:
|
||||
if is_installed('clip_interrogator')==False:
|
||||
import subprocess
|
||||
@@ -36,20 +37,25 @@ try:
|
||||
#检查命令执行结果
|
||||
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
|
||||
|
||||
try:
|
||||
from transformers import AutoProcessor, BlipForConditionalGeneration
|
||||
except:
|
||||
_available=False
|
||||
print('pls check transformers.__version__>=4.36.0:: AutoProcessor, BlipForConditionalGeneration')
|
||||
|
||||
|
||||
|
||||
def load_caption_model(model_path,config,t='blip-base'):
|
||||
dtype=torch.float16 if config.device == 'cuda' else torch.float32
|
||||
|
||||
+177
-85
@@ -14,6 +14,8 @@ import math
|
||||
from .Watcher import FolderWatcher
|
||||
|
||||
|
||||
|
||||
|
||||
FONT_PATH= os.path.abspath(os.path.join(os.path.dirname(__file__),'../assets/王汉宗颜楷体繁.ttf'))
|
||||
|
||||
MAX_RESOLUTION=8192
|
||||
@@ -70,6 +72,48 @@ def color_transfer(source,target):
|
||||
|
||||
|
||||
|
||||
# 组合
|
||||
def create_big_image(image_folder, image_count):
|
||||
# 计算行数和列数
|
||||
rows = math.ceil(math.sqrt(image_count))
|
||||
cols = math.ceil(image_count / rows)
|
||||
|
||||
# 获取每个小图的尺寸
|
||||
small_width = 100
|
||||
small_height = 100
|
||||
|
||||
# 计算大图的尺寸
|
||||
big_width = small_width * cols
|
||||
big_height = small_height * rows
|
||||
|
||||
# 创建一个新的大图
|
||||
big_image = Image.new('RGB', (big_width, big_height))
|
||||
|
||||
# 获取所有图片文件的路径
|
||||
image_files = [f for f in os.listdir(image_folder) if os.path.isfile(os.path.join(image_folder, f))]
|
||||
|
||||
# 遍历所有图片文件
|
||||
for i, image_file in enumerate(image_files):
|
||||
# 打开图片并调整大小
|
||||
image = Image.open(os.path.join(image_folder, image_file))
|
||||
image = image.resize((small_width, small_height))
|
||||
|
||||
# 计算当前小图的位置
|
||||
row = i // cols
|
||||
col = i % cols
|
||||
x = col * small_width
|
||||
y = row * small_height
|
||||
|
||||
# 将小图粘贴到大图上
|
||||
big_image.paste(image, (x, y))
|
||||
|
||||
return big_image
|
||||
|
||||
# # 调用方法并保存大图
|
||||
# image_folder = 'path/to/folder/containing/images'
|
||||
# image_count = 100
|
||||
# big_image = create_big_image(image_folder, image_count)
|
||||
# big_image.save('path/to/save/big_image.jpg')
|
||||
|
||||
|
||||
|
||||
@@ -249,6 +293,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):
|
||||
@@ -390,7 +439,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)
|
||||
|
||||
|
||||
|
||||
@@ -797,79 +847,6 @@ class SmoothMask:
|
||||
|
||||
|
||||
|
||||
class FeatheredMask:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK",),
|
||||
"start_offset":("INT", {"default": 1,
|
||||
"min": -150,
|
||||
"max": 150,
|
||||
"step": 1,
|
||||
"display": "slider"}),
|
||||
"feathering_weight":("FLOAT", {"default": 0.1,
|
||||
"min": 0.0,
|
||||
"max": 1,
|
||||
"step": 0.1,
|
||||
"display": "slider"})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ('MASK',)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
# 运行的函数
|
||||
def run(self,mask,start_offset, feathering_weight):
|
||||
# print(mask.shape,mask.size())
|
||||
|
||||
image=tensor2pil(mask)
|
||||
|
||||
# Open the image using PIL
|
||||
image = image.convert("L")
|
||||
if start_offset>0:
|
||||
image=ImageOps.invert(image)
|
||||
|
||||
# Convert the image to a numpy array
|
||||
image_np = np.array(image)
|
||||
|
||||
# Use Canny edge detection to get black contours
|
||||
edges = cv2.Canny(image_np, 30, 150)
|
||||
|
||||
for i in range(0,abs(start_offset)):
|
||||
# int(100*feathering_weight)
|
||||
a=int(abs(start_offset)*0.1*i)
|
||||
# Dilate the black contours to make them wider
|
||||
kernel = np.ones((a, a), np.uint8)
|
||||
|
||||
dilated_edges = cv2.dilate(edges, kernel, iterations=1)
|
||||
# dilated_edges = cv2.erode(edges, kernel, iterations=1)
|
||||
# Smooth the dilated edges using Gaussian blur
|
||||
smoothed_edges = cv2.GaussianBlur(dilated_edges, (5, 5), 0)
|
||||
|
||||
# Adjust the feathering weight
|
||||
feathering_weight = max(0, min(feathering_weight, 1))
|
||||
|
||||
# Blend the smoothed edges with the original image to achieve feathering effect
|
||||
image_np = cv2.addWeighted(image_np, 1, smoothed_edges, feathering_weight, feathering_weight)
|
||||
|
||||
# Convert the result back to PIL image
|
||||
result_image = Image.fromarray(np.uint8(image_np))
|
||||
result_image=result_image.convert("L")
|
||||
|
||||
if start_offset>0:
|
||||
result_image=ImageOps.invert(result_image)
|
||||
|
||||
mask=pil2tensor(result_image)
|
||||
# print(mask.shape,mask.size())
|
||||
return mask
|
||||
|
||||
|
||||
|
||||
|
||||
class SplitLongMask:
|
||||
@@ -939,7 +916,7 @@ class TransparentImage:
|
||||
|
||||
# 运行的函数
|
||||
def run(self,images,masks,invert,save,filename_prefix,prompt=None, extra_pnginfo=None):
|
||||
print('TransparentImage',images.shape,images.size())
|
||||
# print('TransparentImage',images.shape,images.size(),masks.shape,masks.size())
|
||||
# print(masks.shape,masks.size())
|
||||
|
||||
ui_images=[]
|
||||
@@ -949,11 +926,16 @@ class TransparentImage:
|
||||
masks_new=[]
|
||||
nh=masks.shape[0]//count
|
||||
|
||||
#INPUT_IS_LIST = False, 一个batch传进来
|
||||
if nh*count==masks.shape[0]:
|
||||
masks_new=split_mask_by_new_height(masks,nh)
|
||||
masks_new=masks
|
||||
|
||||
if images.shape[0]==masks.shape[0] and images.shape[1]==masks.shape[1] and images.shape[2]==masks.shape[2]:
|
||||
print('TransparentImage',images.shape,images.size(),masks.shape,masks.size())
|
||||
else:
|
||||
masks_new=split_mask_by_new_height(masks,masks.shape[0])
|
||||
#INPUT_IS_LIST = False, 一个batch传进来
|
||||
if nh*count==masks.shape[0]:
|
||||
masks_new=split_mask_by_new_height(masks,nh)
|
||||
else:
|
||||
masks_new=split_mask_by_new_height(masks,masks.shape[0])
|
||||
|
||||
|
||||
is_save=True if save=='yes' else False
|
||||
@@ -1616,6 +1598,112 @@ class NewLayer:
|
||||
|
||||
return (layer_n,)
|
||||
|
||||
|
||||
def splitImage(image, num):
|
||||
width, height = image.size
|
||||
|
||||
num_rows = int(num ** 0.5)
|
||||
num_cols = int(num / num_rows)
|
||||
|
||||
grid_width = width // num_cols
|
||||
grid_height = height // num_rows
|
||||
|
||||
grid_coordinates = []
|
||||
for i in range(num_rows):
|
||||
for j in range(num_cols):
|
||||
x = j * grid_width
|
||||
y = i * grid_height
|
||||
grid_coordinates.append((x, y, grid_width, grid_height))
|
||||
|
||||
return grid_coordinates
|
||||
|
||||
# # 读取图片
|
||||
# image = Image.open("path_to_your_image.jpg")
|
||||
|
||||
# # 定义要切割的区域数量
|
||||
# num = 9
|
||||
|
||||
# # 切割图片
|
||||
# grid_coordinates = splitImage(image, num)
|
||||
|
||||
# # 输出切割区域坐标
|
||||
# for i, coordinates in enumerate(grid_coordinates):
|
||||
# print(f"Region {i + 1}: x={coordinates[0]}, y={coordinates[1]}, width={coordinates[2]}, height={coordinates[3]}")
|
||||
|
||||
|
||||
class SplitImage:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"num": ("INT",{
|
||||
"default": 4,
|
||||
"min": 1, #Minimum value
|
||||
"max": 500, #Maximum value
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
"seed": ("INT",{
|
||||
"default": 4,
|
||||
"min": 1, #Minimum value
|
||||
"max": 500, #Maximum value
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("_GRID","_GRID",)
|
||||
RETURN_NAMES = ("grids","grid")
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Layer"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
# OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,image,num,seed):
|
||||
image=tensor2pil(image)
|
||||
|
||||
grids=splitImage(image,num)
|
||||
|
||||
if seed>=num:
|
||||
num=int(seed / 500 * num)-1
|
||||
else:
|
||||
num=seed-1
|
||||
|
||||
num=max(0,num)
|
||||
|
||||
g=grids[num]
|
||||
|
||||
return (grids,g,)
|
||||
|
||||
|
||||
class GridOutput:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"grid": ("_GRID",)
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT","INT","INT","INT",)
|
||||
RETURN_NAMES = ("x","y","width","height",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Layer"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
# OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,grid):
|
||||
x,y,w,h=grid
|
||||
return (x,y,w,h,)
|
||||
|
||||
class ShowLayer:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@@ -1973,15 +2061,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"
|
||||
|
||||
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"]):
|
||||
|
||||
@@ -1993,16 +2081,18 @@ 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 ims in image:
|
||||
for im in ims:
|
||||
@@ -2010,15 +2100,16 @@ class ResizeImage:
|
||||
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)
|
||||
|
||||
a_im=pil2tensor(a_im)
|
||||
average_images.append(a_im)
|
||||
hexs.append(hex)
|
||||
|
||||
return (imgs,average_images,)
|
||||
return (imgs,average_images,hexs,)
|
||||
|
||||
|
||||
class MirroredImage:
|
||||
@@ -2120,3 +2211,4 @@ class ImageColorTransfer:
|
||||
return (res,)
|
||||
|
||||
|
||||
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
|
||||
import scipy.ndimage
|
||||
import torch
|
||||
|
||||
from nodes import MAX_RESOLUTION
|
||||
|
||||
import numpy as np
|
||||
# from PIL import Image, ImageDraw
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
from comfy.cli_args import args
|
||||
import cv2
|
||||
|
||||
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
# Convert PIL to Tensor
|
||||
def pil2tensor(image):
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
|
||||
def grow(mask, expand, tapered_corners):
|
||||
c = 0 if tapered_corners else 1
|
||||
kernel = np.array([[c, 1, c],
|
||||
[1, 1, 1],
|
||||
[c, 1, c]])
|
||||
mask = mask.reshape((-1, mask.shape[-2], mask.shape[-1]))
|
||||
out = []
|
||||
for m in mask:
|
||||
output = m.numpy()
|
||||
for _ in range(abs(expand)):
|
||||
if expand < 0:
|
||||
output = scipy.ndimage.grey_erosion(output, footprint=kernel)
|
||||
else:
|
||||
output = scipy.ndimage.grey_dilation(output, footprint=kernel)
|
||||
output = torch.from_numpy(output)
|
||||
out.append(output)
|
||||
return torch.stack(out, dim=0)
|
||||
|
||||
def combine(destination, source, x, y):
|
||||
output = destination.reshape((-1, destination.shape[-2], destination.shape[-1])).clone()
|
||||
source = source.reshape((-1, source.shape[-2], source.shape[-1]))
|
||||
|
||||
left, top = (x, y,)
|
||||
right, bottom = (min(left + source.shape[-1], destination.shape[-1]), min(top + source.shape[-2], destination.shape[-2]))
|
||||
visible_width, visible_height = (right - left, bottom - top,)
|
||||
|
||||
source_portion = source[:, :visible_height, :visible_width]
|
||||
destination_portion = destination[:, top:bottom, left:right]
|
||||
|
||||
#operation == "subtract":
|
||||
output[:, top:bottom, left:right] = destination_portion - source_portion
|
||||
|
||||
output = torch.clamp(output, 0.0, 1.0)
|
||||
|
||||
return output
|
||||
|
||||
class OutlineMask:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK",),
|
||||
"outline_width":("INT", {"default": 10,"min": 1, "max": MAX_RESOLUTION, "step": 1}),
|
||||
"tapered_corners": ("BOOLEAN", {"default": True}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ('MASK',)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
# 运行的函数
|
||||
def run(self, mask, outline_width, tapered_corners):
|
||||
|
||||
m1=grow(mask,outline_width,tapered_corners)
|
||||
m2=grow(mask,-outline_width,tapered_corners)
|
||||
|
||||
m3=combine(m1,m2,0,0)
|
||||
|
||||
return (m3,)
|
||||
|
||||
|
||||
|
||||
|
||||
class FeatheredMask:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK",),
|
||||
"start_offset":("INT", {"default": 1,
|
||||
"min": -150,
|
||||
"max": 150,
|
||||
"step": 1,
|
||||
"display": "slider"}),
|
||||
"feathering_weight":("FLOAT", {"default": 0.1,
|
||||
"min": 0.0,
|
||||
"max": 1,
|
||||
"step": 0.1,
|
||||
"display": "slider"})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ('MASK',)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
# 运行的函数
|
||||
def run(self,mask,start_offset, feathering_weight):
|
||||
# print(mask.shape,mask.size())
|
||||
|
||||
num,_,_=mask.size()
|
||||
|
||||
masks=[]
|
||||
|
||||
for i in range(num):
|
||||
mm=mask[i]
|
||||
image=tensor2pil(mm)
|
||||
|
||||
# Open the image using PIL
|
||||
image = image.convert("L")
|
||||
if start_offset>0:
|
||||
image=ImageOps.invert(image)
|
||||
|
||||
# Convert the image to a numpy array
|
||||
image_np = np.array(image)
|
||||
|
||||
# Use Canny edge detection to get black contours
|
||||
edges = cv2.Canny(image_np, 30, 150)
|
||||
|
||||
for i in range(0,abs(start_offset)):
|
||||
# int(100*feathering_weight)
|
||||
a=int(abs(start_offset)*0.1*i)
|
||||
# Dilate the black contours to make them wider
|
||||
kernel = np.ones((a, a), np.uint8)
|
||||
|
||||
dilated_edges = cv2.dilate(edges, kernel, iterations=1)
|
||||
# dilated_edges = cv2.erode(edges, kernel, iterations=1)
|
||||
# Smooth the dilated edges using Gaussian blur
|
||||
smoothed_edges = cv2.GaussianBlur(dilated_edges, (5, 5), 0)
|
||||
|
||||
# Adjust the feathering weight
|
||||
feathering_weight = max(0, min(feathering_weight, 1))
|
||||
|
||||
# Blend the smoothed edges with the original image to achieve feathering effect
|
||||
image_np = cv2.addWeighted(image_np, 1, smoothed_edges, feathering_weight, feathering_weight)
|
||||
|
||||
# Convert the result back to PIL image
|
||||
result_image = Image.fromarray(np.uint8(image_np))
|
||||
result_image=result_image.convert("L")
|
||||
|
||||
if start_offset>0:
|
||||
result_image=ImageOps.invert(result_image)
|
||||
|
||||
result_image=result_image.convert("L")
|
||||
mt=pil2tensor(result_image)
|
||||
masks.append(mt)
|
||||
|
||||
# print( mt.size())
|
||||
return (masks,)
|
||||
+86
-55
@@ -12,6 +12,30 @@ from PIL.PngImagePlugin import PngInfo
|
||||
# req = request.Request("http://127.0.0.1:8188/prompt", data=data)
|
||||
# request.urlopen(req)
|
||||
|
||||
embeddings_path=os.path.join(folder_paths.models_dir, "embeddings")
|
||||
|
||||
def get_files_with_extension(directory, extension):
|
||||
file_list = []
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
if file.endswith(extension):
|
||||
file_name = os.path.splitext(file)[0]
|
||||
file_list.append(file_name)
|
||||
return file_list
|
||||
|
||||
def join_with_(text_list,delimiter):
|
||||
joined_text = delimiter.join(text_list)
|
||||
return joined_text
|
||||
|
||||
|
||||
class AnyType(str):
|
||||
"""A special class that is always equal in not equal comparisons. Credit to pythongosssss"""
|
||||
|
||||
def __ne__(self, __value: object) -> bool:
|
||||
return False
|
||||
|
||||
any_type = AnyType("*")
|
||||
|
||||
|
||||
default_prompt1='''Swing
|
||||
Slide
|
||||
@@ -54,7 +78,7 @@ def addWeight(text, weight=1):
|
||||
if weight == 1:
|
||||
return text
|
||||
else:
|
||||
return f"({text}:{round(weight,2)})"
|
||||
return f"({text}:{round(weight,3)})"
|
||||
|
||||
def prompt_delete_words(sentence, new_words_length):
|
||||
# 使用逗号分割句子,并去除空格
|
||||
@@ -297,7 +321,9 @@ class RandomPrompt:
|
||||
"default": 'sticker, Cartoon, ``'
|
||||
}),
|
||||
"random_sample": (["enable", "disable"],),
|
||||
}
|
||||
# "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "step": 1}),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -334,7 +360,10 @@ class RandomPrompt:
|
||||
for w2 in words2:
|
||||
w2=w2.strip()
|
||||
if '``' not in w2:
|
||||
w2=w2+',``'
|
||||
if w2=="":
|
||||
w2='``'
|
||||
else:
|
||||
w2=w2+',``'
|
||||
if w1!='' and w2!='':
|
||||
prompts.append(w2.replace('``', w1))
|
||||
pbar.update(1)
|
||||
@@ -357,62 +386,64 @@ class RandomPrompt:
|
||||
|
||||
|
||||
|
||||
# class RunWorkflow:
|
||||
# @classmethod
|
||||
# def INPUT_TYPES(s):
|
||||
# return {
|
||||
# "required": {
|
||||
# "workflow": ("STRING", {
|
||||
# "multiline": False,
|
||||
# "default": ''
|
||||
# }),
|
||||
# "prompt": ("STRING", {
|
||||
# "multiline": False,
|
||||
# "default": ''
|
||||
# }),
|
||||
# "image": ("IMAGE",),
|
||||
# "input_node": ("STRING", {
|
||||
# "multiline": False,
|
||||
# "default": ''
|
||||
# }),
|
||||
# "output_node": ("STRING", {
|
||||
# "multiline": False,
|
||||
# "default": ''
|
||||
# }),
|
||||
# },
|
||||
|
||||
class EmbeddingPrompt:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"embedding":(get_files_with_extension(embeddings_path,'.pt'),),
|
||||
"weight": ("FLOAT", {"default": 1, "min": -2, "max": 2,"step":0.01 ,"display": "slider"}),
|
||||
},
|
||||
|
||||
# }
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
# OUTPUT_NODE = True
|
||||
|
||||
# 运行的函数
|
||||
def run(self,embedding,weight):
|
||||
weight = round(weight, 3)
|
||||
prompt='embedding:'+embedding
|
||||
if weight!=1:
|
||||
prompt='('+prompt+':'+str(weight)+')'
|
||||
prompt=" "+prompt+' '
|
||||
# return (new_prompt)
|
||||
return (prompt,)
|
||||
|
||||
RETURN_TYPES = (any_type,)
|
||||
|
||||
class JoinWithDelimiter:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"text_list": (any_type,),
|
||||
"delimiter":(["newline","comma"],),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
|
||||
# RETURN_TYPES = ("IMAGE","STRING",)
|
||||
FUNCTION = "run"
|
||||
|
||||
# FUNCTION = "run"
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
# CATEGORY = "♾️Mixlab/workflow"
|
||||
|
||||
# OUTPUT_IS_LIST = (True,)
|
||||
# OUTPUT_NODE = True
|
||||
|
||||
|
||||
# # 运行的函数
|
||||
# def run(self,workflow,prompt,image,input_node,output_node):
|
||||
# print('#运行的函数',prompt,image,input_node,output_node)
|
||||
# workflow=json.loads(workflow)
|
||||
# input_node=input_node.split(".")
|
||||
# workflow[input_node[0]][input_node[1]][input_node[2]]=prompt
|
||||
|
||||
# workflow_new={}
|
||||
# # 遍历,seed设为随机
|
||||
# for key, value in workflow.items():
|
||||
# if 'inputs' in value:
|
||||
# if 'seed' in value['inputs']:
|
||||
# value['inputs']['seed']= random.randint(1, 18446744073709551614)
|
||||
# workflow_new[key]=value
|
||||
|
||||
# queue_prompt(workflow_new)
|
||||
# print('#运行的函数',workflow_new[input_node[0]])
|
||||
|
||||
# # return (new_prompt)
|
||||
# return {"ui":{"images": []},"result": ([image],['text'],)}
|
||||
INPUT_IS_LIST = True # 当true的时候,输入时list,当false的时候,如果输入是list,则会自动包一层for循环调用
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,text_list,delimiter):
|
||||
delimiter=delimiter[0]
|
||||
if delimiter =='newline':
|
||||
delimiter='\n'
|
||||
elif delimiter=='comma':
|
||||
delimiter=','
|
||||
t=''
|
||||
if isinstance(text_list, list):
|
||||
t=join_with_(text_list,delimiter)
|
||||
return (t,)
|
||||
@@ -0,0 +1,164 @@
|
||||
import os,sys
|
||||
import folder_paths
|
||||
|
||||
from PIL import Image
|
||||
import importlib.util
|
||||
|
||||
import comfy.utils
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
|
||||
U2NET_HOME=os.path.join(folder_paths.models_dir, "rembg")
|
||||
os.environ["U2NET_HOME"] = U2NET_HOME
|
||||
|
||||
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('rembg')==False:
|
||||
import subprocess
|
||||
|
||||
# 安装
|
||||
print('#pip install rembg[gpu]')
|
||||
|
||||
result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', 'rembg[gpu]'], capture_output=True, text=True)
|
||||
|
||||
#检查命令执行结果
|
||||
if result.returncode == 0:
|
||||
print("#install success")
|
||||
from rembg import new_session, remove
|
||||
_available=True
|
||||
else:
|
||||
print("#install error")
|
||||
|
||||
else:
|
||||
from rembg import new_session, remove
|
||||
_available=True
|
||||
|
||||
except:
|
||||
_available=False
|
||||
|
||||
|
||||
def run_bg(model_name= "unet",images=[]):
|
||||
# model_name = "unet" # "isnet-general-use"
|
||||
rembg_session = new_session(model_name)
|
||||
masks=[]
|
||||
rgba_images=[]
|
||||
rgb_images=[]
|
||||
# 进度条
|
||||
pbar = comfy.utils.ProgressBar(len(images) )
|
||||
for img in images:
|
||||
# use the post_process_mask argument to post process the mask to get better results.
|
||||
mask = remove(img, session=rembg_session,only_mask=True,post_process_mask=True)
|
||||
# mask=mask.convert('L')
|
||||
# masks.append(mask)
|
||||
if model_name=="u2net_cloth_seg":
|
||||
width, original_height = mask.size
|
||||
num_slices = original_height // img.height
|
||||
for i in range(num_slices):
|
||||
top = i * img.height
|
||||
bottom = (i + 1) * img.height
|
||||
slice_image = mask.crop((0, top, width, bottom))
|
||||
slice_mask=slice_image.convert('L')
|
||||
masks.append(slice_mask)
|
||||
|
||||
# rgba图
|
||||
image_rgba = img.convert("RGBA")
|
||||
image_rgba.putalpha(slice_mask)
|
||||
rgba_images.append(image_rgba)
|
||||
|
||||
#rgb
|
||||
rgb_image = Image.new("RGB", image_rgba.size, (0, 0, 0))
|
||||
rgb_image.paste(image_rgba, mask=image_rgba.split()[3])
|
||||
rgb_images.append(rgb_image)
|
||||
|
||||
else:
|
||||
mask=mask.convert('L')
|
||||
# mask.save(output_path)
|
||||
masks.append(mask)
|
||||
|
||||
# rgba图
|
||||
image_rgba = img.convert("RGBA")
|
||||
image_rgba.putalpha(mask)
|
||||
rgba_images.append(image_rgba)
|
||||
|
||||
#rgb
|
||||
rgb_image = Image.new("RGB", image_rgba.size, (0, 0, 0))
|
||||
rgb_image.paste(image_rgba, mask=image_rgba.split()[3])
|
||||
rgb_images.append(rgb_image)
|
||||
|
||||
pbar.update(1)
|
||||
return (masks,rgba_images,rgb_images)
|
||||
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
# Convert PIL to Tensor
|
||||
def pil2tensor(image):
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
|
||||
class RembgNode_:
|
||||
|
||||
global _available
|
||||
available=_available
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"image": ("IMAGE",),
|
||||
"model_name": (["u2net",
|
||||
"u2netp",
|
||||
"u2net_human_seg",
|
||||
"u2net_cloth_seg",
|
||||
"silueta",
|
||||
"isnet-general-use",
|
||||
"isnet-anime",
|
||||
# "sam"
|
||||
],),
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK","IMAGE","RGBA",)
|
||||
RETURN_NAMES = ("masks","images","RGBAs")
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,True,True,)
|
||||
|
||||
def run(self,image,model_name):
|
||||
# 兼容list输入和batch输入
|
||||
|
||||
model_name=model_name[0]
|
||||
|
||||
images=[]
|
||||
|
||||
for ims in image:
|
||||
for im in ims:
|
||||
im=tensor2pil(im)
|
||||
images.append(im)
|
||||
|
||||
masks,rgba_images,rgb_images=run_bg(model_name,images)
|
||||
|
||||
masks=[pil2tensor(m) for m in masks]
|
||||
|
||||
rgba_images=[pil2tensor(m) for m in rgba_images]
|
||||
|
||||
rgb_images=[pil2tensor(m) for m in rgb_images]
|
||||
|
||||
return (masks,rgb_images,rgba_images,)
|
||||
+113
-21
@@ -6,7 +6,7 @@ import os,sys
|
||||
import folder_paths
|
||||
|
||||
# from PIL import Image
|
||||
# import importlib.util
|
||||
import importlib.util
|
||||
|
||||
import comfy.utils
|
||||
# import numpy as np
|
||||
@@ -29,18 +29,50 @@ if not os.path.exists(zh_en_model_path):
|
||||
|
||||
|
||||
|
||||
def translate(zh_en_tokenizer,zh_en_model,texts):
|
||||
def is_installed(package):
|
||||
try:
|
||||
spec = importlib.util.find_spec(package)
|
||||
except ModuleNotFoundError:
|
||||
return False
|
||||
return spec is not None
|
||||
|
||||
|
||||
try:
|
||||
if is_installed('sentencepiece')==False:
|
||||
import subprocess
|
||||
|
||||
# 安装
|
||||
print('#pip install sentencepiece')
|
||||
|
||||
result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', 'sentencepiece'], capture_output=True, text=True)
|
||||
|
||||
#检查命令执行结果
|
||||
if result.returncode == 0 and is_installed('sentencepiece'):
|
||||
print("#install success")
|
||||
_available=True
|
||||
else:
|
||||
print("#install error")
|
||||
_available=False
|
||||
|
||||
else:
|
||||
_available=True
|
||||
|
||||
except:
|
||||
_available=False
|
||||
|
||||
|
||||
|
||||
def translate(zh_en_tokenizer,zh_en_model,text):
|
||||
with torch.no_grad():
|
||||
encoded = zh_en_tokenizer(texts, return_tensors="pt")
|
||||
encoded = zh_en_tokenizer([text], 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)
|
||||
return zh_en_tokenizer.batch_decode(sequences, skip_special_tokens=True)[0]
|
||||
|
||||
# input = "青春不能回头,所以青春没有终点。 ——《火影忍者》"
|
||||
# print(input, translate(input))
|
||||
|
||||
|
||||
|
||||
def text_generate(text_pipe,input,seed=None):
|
||||
|
||||
if seed==None:
|
||||
@@ -66,6 +98,55 @@ def text_generate(text_pipe,input,seed=None):
|
||||
|
||||
# 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:
|
||||
@@ -76,12 +157,13 @@ class ChinesePrompt:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"text": ("STRING",{"multiline": True,"default": "", "dynamicPrompts": False}),
|
||||
|
||||
"text": ("STRING",{"multiline": True,"default": "", "dynamicPrompts": False}),
|
||||
"generation": (["on","off"],{"default": "off"}),
|
||||
},
|
||||
|
||||
|
||||
"optional":{
|
||||
"seed":("INT", {"default": 100, "min": 100, "max": 1000000}),
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
@@ -102,17 +184,20 @@ class ChinesePrompt:
|
||||
zh_en_model=None
|
||||
zh_en_tokenizer=None
|
||||
|
||||
def run(self,text,seed):
|
||||
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)
|
||||
|
||||
|
||||
texts = [correct_prompt_syntax(t) for t in text]
|
||||
print('correct_prompt_syntax::',texts)
|
||||
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_tokenizer = AutoTokenizer.from_pretrained(zh_en_model_path,padding=True, truncation=True)
|
||||
|
||||
zh_en_model.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
# zh_en_tokenizer.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
@@ -124,24 +209,31 @@ class ChinesePrompt:
|
||||
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)
|
||||
en_texts=[]
|
||||
for t in texts:
|
||||
en_text=translate(zh_en_tokenizer,zh_en_model,t)
|
||||
en_texts.append(en_text)
|
||||
|
||||
zh_en_model.to('cpu')
|
||||
print("test en_text",en_texts)
|
||||
# en_text.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
pbar.update(1)
|
||||
for t in en_text:
|
||||
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)
|
||||
for t in en_texts:
|
||||
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(p) for p in prompt_result]
|
||||
|
||||
|
||||
|
||||
return {
|
||||
"ui":{
|
||||
"prompt": prompt_result
|
||||
|
||||
+26
-13
@@ -5,7 +5,7 @@ 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):
|
||||
@@ -131,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
|
||||
|
||||
|
||||
@@ -336,11 +341,18 @@ class MultiplicationNode:
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"numberA":(any_type,),
|
||||
"numberB":("FLOAT", {
|
||||
"multiply_by":("FLOAT", {
|
||||
"default": 0,
|
||||
"min": -1, #Minimum value
|
||||
"min": -2, #Minimum value
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.1, #Slider's step
|
||||
"step": 0.01, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
"add_by":("FLOAT", {
|
||||
"default": 0,
|
||||
"min": -2000, #Minimum value
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.01, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
})
|
||||
},
|
||||
@@ -355,9 +367,9 @@ class MultiplicationNode:
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,False,)
|
||||
|
||||
def run(self,numberA,numberB):
|
||||
b=int(numberA*numberB)
|
||||
a=float(numberA*numberB)
|
||||
def run(self,numberA,multiply_by,add_by):
|
||||
b=int(numberA*multiply_by+add_by)
|
||||
a=float(numberA*multiply_by+add_by)
|
||||
return (a,b,)
|
||||
|
||||
class TextInput:
|
||||
@@ -485,7 +497,7 @@ class AppInfo:
|
||||
},
|
||||
|
||||
"optional":{
|
||||
"LOGO": ("IMAGE",),
|
||||
"IMAGE": ("IMAGE",),
|
||||
"description":("STRING",{"multiline": True,"default": "","dynamicPrompts": False}),
|
||||
"version":("INT", {
|
||||
"default": 1,
|
||||
@@ -513,12 +525,12 @@ class AppInfo:
|
||||
INPUT_IS_LIST = True
|
||||
# OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,name,input_ids,output_ids,LOGO,description,version,share_prefix,link,category,auto_save):
|
||||
def run(self,name,input_ids,output_ids,IMAGE,description,version,share_prefix,link,category,auto_save):
|
||||
name=name[0]
|
||||
|
||||
im=None
|
||||
if LOGO:
|
||||
im=LOGO[0][0]
|
||||
if IMAGE:
|
||||
im=IMAGE[0][0]
|
||||
#TODO batch 的方式需要处理
|
||||
im=create_temp_file(im)
|
||||
# image [img,] img[batch,w,h,a] 列表里面是batch,
|
||||
@@ -573,7 +585,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)
|
||||
|
||||
+2
-2
@@ -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
|
||||
+33
-8
@@ -391,14 +391,14 @@
|
||||
<a class="link" href="https://www.mixcomfy.com" target="_blank">ComfyUI中文爱好者社区推荐</a>
|
||||
</div>
|
||||
|
||||
<a target="_blank" href="https://github.com/shadowcz007/comfyui-mixlab-nodes" style="text-decoration: none;
|
||||
<a target="_blank" href="https://www.mixcomfy.com/blog/" style="text-decoration: none;
|
||||
color: black;font-size:12px">
|
||||
<svg height="32" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="32" data-view-component="true"
|
||||
class="octicon octicon-mark-github v-align-middle color-fg-default">
|
||||
<path
|
||||
d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z">
|
||||
</path>
|
||||
</svg> Code
|
||||
</svg> Community
|
||||
|
||||
</a>
|
||||
</div>
|
||||
@@ -761,19 +761,44 @@
|
||||
} catch (error) {
|
||||
isURL = false;
|
||||
}
|
||||
|
||||
let isAElement = undefined;
|
||||
try {
|
||||
let div = document.createElement('div');
|
||||
div.innerHTML = link;
|
||||
let a = div.querySelector('a');
|
||||
if (a.href) {
|
||||
new URL(a.href);
|
||||
isAElement = div.innerHTML;
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
// new URL(link)
|
||||
if (isURL) {
|
||||
if (isURL || isAElement) {
|
||||
const linkBtn = document.createElement('button');
|
||||
// linkBtn.href = link;
|
||||
linkBtn.innerText = 'go to'
|
||||
|
||||
if (isURL) {
|
||||
// linkBtn.href = link;
|
||||
linkBtn.innerText = 'go to'
|
||||
} else if (isAElement) {
|
||||
linkBtn.innerHTML = isAElement
|
||||
}
|
||||
|
||||
action.appendChild(linkBtn)
|
||||
linkBtn.style.marginLeft = '18px';
|
||||
linkBtn.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
window.open(link);
|
||||
|
||||
if (isURL) {
|
||||
e.preventDefault();
|
||||
window.open(link);
|
||||
}
|
||||
// if(isURL) window.open(link);
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
const output_card = document.createElement("div");
|
||||
output_card.className = 'output_card'
|
||||
container.appendChild(output_card)
|
||||
@@ -1826,7 +1851,7 @@
|
||||
p.className = 'prompt_image'
|
||||
p.innerText = prompt;
|
||||
a.appendChild(p)
|
||||
img.alt=prompt
|
||||
img.alt = prompt
|
||||
}
|
||||
|
||||
// imgDiv.parentElement.appendChild(a);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
|
||||
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
|
||||
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
|
||||
|
||||
const version = 'v0.12.0'
|
||||
const version = 'v0.14.0'
|
||||
|
||||
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
|
||||
.then(response => response.json())
|
||||
|
||||
@@ -1331,7 +1331,13 @@ app.registerExtension({
|
||||
let w = 360,
|
||||
s = widget.preview.videoWidth / widget.preview.videoHeight,
|
||||
h = w / s || w
|
||||
console.log(h)
|
||||
// console.log(h)
|
||||
|
||||
if (!window.documentPictureInPicture) {
|
||||
window.alert(
|
||||
'This feature is available only in secure contexts (HTTPS), in some or all supporting browsers. https://developer.mozilla.org/en-US/docs/Web/API/Document_Picture-in-Picture_API'
|
||||
)
|
||||
}
|
||||
|
||||
const pipWindow = await documentPictureInPicture.requestWindow({
|
||||
width: w,
|
||||
@@ -2137,8 +2143,8 @@ const node = {
|
||||
loadedGraphNode (node, app) {
|
||||
if (node.type === 'RandomPrompt') {
|
||||
try {
|
||||
let max_count = node.widgets.filter(w => w.name === "max_count")[0];
|
||||
max_count.value= node.widgets_values[0]
|
||||
let max_count = node.widgets.filter(w => w.name === 'max_count')[0]
|
||||
max_count.value = node.widgets_values[0]
|
||||
// console.log('RandomPrompt',max_count,node.widgets_values[0])
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
|
||||
@@ -19,7 +19,7 @@ function loadCSS (url) {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
width: calc(100% - 32px);
|
||||
max-width: 400px;
|
||||
max-width: 980px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
@@ -178,8 +178,7 @@ app.registerExtension({
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = async function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
name
|
||||
|
||||
|
||||
const mutable_prompt = this.widgets.filter(
|
||||
w => w.name == 'mutable_prompt'
|
||||
)[0]
|
||||
@@ -267,16 +266,7 @@ app.registerExtension({
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'RandomPrompt') {
|
||||
// try {
|
||||
// let mutable_prompt = node.widgets.filter(w => w.name === 'mutable_prompt')[0]
|
||||
// // let ks = getLocalData(`_mixlab_PromptSlide`)
|
||||
// let uploadWidget = node.widgets.filter(w => w.name == 'upload')[0]
|
||||
// // console.log('##widget', uploadWidget.value)
|
||||
// let keywords = JSON.parse(uploadWidget.value)
|
||||
// if (keywords && keywords[0]) {
|
||||
// mutable_prompt.value=keywords.join('\n')
|
||||
// }
|
||||
// } catch (error) {}
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -739,8 +739,9 @@ app.registerExtension({
|
||||
if (category === '0') {
|
||||
apps_opts.push(
|
||||
...Array.from(apps_map[category], a => {
|
||||
// console.log('#1级',a)
|
||||
return {
|
||||
content: a.name,
|
||||
content: `${a.name}_${a.version}`,
|
||||
has_submenu: false,
|
||||
callback: async () => {
|
||||
try {
|
||||
@@ -768,8 +769,9 @@ app.registerExtension({
|
||||
disabled: false,
|
||||
submenu: {
|
||||
options: Array.from(apps_map[category], a => {
|
||||
// console.log('#二级',a)
|
||||
return {
|
||||
content: a.name,
|
||||
content: `${a.name}_${a.version}`,
|
||||
callback: async () => {
|
||||
try {
|
||||
let item = (await get_my_app(a.filename, a.category))[0]
|
||||
|
||||
Reference in New Issue
Block a user