Compare commits

..
7 Commits
Author SHA1 Message Date
shadowcz007 dd816e95cd Update README.md 2024-04-27 23:43:40 +08:00
shadowcz007 6d1b51890d Update checkVersion_mixlab.js 2024-04-27 23:38:00 +08:00
shadowcz007 11f03ec99a 支持正片叠底 2024-04-25 21:23:40 +08:00
shadowcz007 bd192f43e7 优化 2024-04-24 23:10:03 +08:00
shadowcz007 36e4b11983 gridout can export mask 2024-04-23 12:14:50 +08:00
shadowcz007 97397ba8c2 Update ImageNode.py 2024-04-22 12:32:48 +08:00
shadowcz007 052eee4111 Update __init__.py 2024-04-22 12:31:40 +08:00
5 changed files with 190 additions and 54 deletions
+3 -1
View File
@@ -3,13 +3,15 @@
> [Mixlab nodes discord](https://discord.gg/cXs9vZSqeK)
####
[comfyui-sd-prompt-mixlab](https://github.com/shadowcz007/comfyui-sd-prompt-mixlab)
[comfyui-Image-reward](https://github.com/shadowcz007/comfyui-Image-reward)
[comfyui-ultralytics-yolo](https://github.com/shadowcz007/comfyui-ultralytics-yolo)
[comfyui-moondream](https://github.com/shadowcz007/comfyui-moondream)
[comfyui-CLIPSeg](https://github.com/shadowcz007/comfyui-CLIPSeg)
<!-- [comfyui-CLIPSeg](https://github.com/shadowcz007/comfyui-CLIPSeg) -->
## 🚀🚗🚚🏃 Workflow-to-APP
+25 -23
View File
@@ -596,7 +596,7 @@ from .nodes.ScreenShareNode import ScreenShareNode,FloatingVideo
from .nodes.ChatGPT import ChatGPTNode,ShowTextForGPT,CharacterInText,TextSplitByDelimiter
from .nodes.Audio import GamePal,SpeechRecognition,SpeechSynthesis
from .nodes.Utils import IncrementingListNode,ListSplit,CreateLoraNames,CreateSampler_names,CreateCkptNames,CreateSeedNode,TESTNODE_,TESTNODE_TOKEN,AppInfo,IntNumber,FloatSlider,TextInput,ColorInput,FontInput,TextToNumber,DynamicDelayProcessor,LimitNumber,SwitchByIndex,MultiplicationNode
from .nodes.Mask import MaskListReplace,MaskListMerge,OutlineMask,FeatheredMask
from .nodes.Mask import PreviewMask_,MaskListReplace,MaskListMerge,OutlineMask,FeatheredMask
from .nodes.Style import ApplyVisualStylePrompting,StyleAlignedReferenceSampler,StyleAlignedBatchAlign,StyleAlignedSampleReferenceLatents
@@ -685,7 +685,8 @@ NODE_CLASS_MAPPINGS = {
"MaskListReplace_":MaskListReplace,
"ImageListReplace_":ImageListReplace,
"VAEEncodeForInpaint_Frames":VAEEncodeForInpaint_Frames,
"IncrementingListNode_":IncrementingListNode
"IncrementingListNode_":IncrementingListNode,
"PreviewMask_":PreviewMask_
# "GamePal":GamePal
}
@@ -700,7 +701,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"SaveImageAndMetadata_":"Save Image Output ♾️MixlabApp",
"ResizeImageMixlab":"Resize Image ♾️Mixlab",
"RandomPrompt": "Random Prompt ♾️Mixlab",
"PromptImage":"Output Prompt and Image",
"PromptImage":"Output Prompt and Image ♾️Mixlab",
"SplitLongMask":"Splitting a long image into sections",
"VAELoaderConsistencyDecoder":"Consistency Decoder Loader",
"VAEDecodeConsistencyDecoder":"Consistency Decoder Decode",
@@ -712,7 +713,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"SpeechSynthesis":"SpeechSynthesis ♾️Mixlab",
"SpeechRecognition":"SpeechRecognition ♾️Mixlab",
"3DImage":"3DImage ♾️Mixlab",
"CompositeImages_":"Composite Images",
"CompositeImages_":"Composite Images ♾️Mixlab",
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab",
"LaMaInpainting":"LaMaInpainting ♾️Mixlab",
"PromptSlide":"Prompt Slide ♾️Mixlab",
@@ -720,26 +721,27 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"ChinesePrompt_Mix":"Chinese Prompt ♾️Mixlab",
"GamePal":"GamePal ♾️Mixlab",
"RembgNode_Mix":"Remove Background ♾️Mixlab",
"LoraNames_":"LoraName",
"ApplyVisualStylePrompting_":"Apply VisualStyle Prompting",
"StyleAlignedReferenceSampler_": "StyleAligned Reference Sampler",
"StyleAlignedSampleReferenceLatents_": "StyleAligned Sample Reference Latents",
"StyleAlignedBatchAlign_": "StyleAligned Batch Align",
"LoadVideoAndSegment_":"Load Video And Segment",
"VideoCombine_Adv":"Video Combine",
"MaskListMerge_":"MaskList to Mask",
"ListSplit_":"Split List",
"MaskListReplace_":"MaskList Replace",
"ImageListReplace_":"ImageList Replace",
"SwitchByIndex":"List Switch By Index",
"LoraNames_":"LoraName ♾️Mixlab",
"ApplyVisualStylePrompting_":"Apply VisualStyle Prompting ♾️Mixlab",
"StyleAlignedReferenceSampler_": "StyleAligned Reference Sampler ♾️Mixlab",
"StyleAlignedSampleReferenceLatents_": "StyleAligned Sample Reference Latents ♾️Mixlab",
"StyleAlignedBatchAlign_": "StyleAligned Batch Align ♾️Mixlab",
"LoadVideoAndSegment_":"Load Video And Segment ♾️Mixlab",
"VideoCombine_Adv":"Video Combine ♾️Mixlab",
"MaskListMerge_":"MaskList to Mask ♾️Mixlab",
"ListSplit_":"Split List ♾️Mixlab",
"MaskListReplace_":"MaskList Replace ♾️Mixlab",
"ImageListReplace_":"ImageList Replace ♾️Mixlab",
"SwitchByIndex":"List Switch By Index ♾️Mixlab",
"GLIGENTextBoxApply_Advanced":"GLIGEN TextBox Apply ♾️Mixlab",
"GridDisplayAndSave":"Grid Display And Save",
"GridInput":"Grid Input",
"GridOutput":"Grid Output",
"GetImageSize_":"Get Image Size",
"VAEEncodeForInpaint_Frames":"VAE Encode For Inpaint Frames",
"IncrementingListNode_":"Create Incrementing Number List",
"LoadImagesToBatch":"Load Images(base64) ♾️Mixlab"
"GridDisplayAndSave":"Grid Display And Save ♾️Mixlab",
"GridInput":"Grid Input ♾️Mixlab",
"GridOutput":"Grid Output ♾️Mixlab",
"GetImageSize_":"Get Image Size ♾️Mixlab",
"VAEEncodeForInpaint_Frames":"VAE Encode For Inpaint Frames ♾️Mixlab",
"IncrementingListNode_":"Create Incrementing Number List ♾️Mixlab",
"LoadImagesToBatch":"Load Images(base64) ♾️Mixlab",
"PreviewMask_":"Preview Mask"
}
# web ui的节点功能
+122 -23
View File
@@ -16,6 +16,19 @@ import math,glob
from .Watcher import FolderWatcher
import hashlib
# 将PIL图片转换为OpenCV格式
def pil_to_opencv(image):
open_cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
return open_cv_image
# 将OpenCV格式图片转换为PIL格式
def opencv_to_pil(image):
pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
return pil_image
def composite_images(foreground, background, mask):
width,height=foreground.size
@@ -620,7 +633,7 @@ def detect_faces(image):
def areaToMask(x,y,w,h,image):
# 创建一个与原图片大小相同的空白图片
mask = Image.new('1', image.size)
mask = Image.new('L', image.size)
# 创建一个可用于绘制的对象
draw = ImageDraw.Draw(mask)
@@ -653,7 +666,46 @@ def areaToMask(x,y,w,h,image):
# # bg_image.save("output.jpg")
# return bg_image
def merge_images(bg_image, layer_image, mask, x, y, width, height, scale_option):
import cv2
import numpy as np
# ps的正片叠底
# 可以基于https://www.cnblogs.com/jsxyhelu/p/16947810.html ,用gpt写python代码
def multiply_blend(image1, image2):
image1=pil_to_opencv(image1)
image2=pil_to_opencv(image2)
# 将图像转换为浮点型
image1 = image1.astype(float)
image2 = image2.astype(float)
if image1.shape != image2.shape:
image1 = cv2.resize(image1, (image2.shape[1], image2.shape[0]))
# 归一化图像
image1 /= 255.0
image2 /= 255.0
# 正片叠底混合
blended = image1 * image2
# 将图像还原为8位无符号整数
blended = (blended * 255).astype(np.uint8)
blended=opencv_to_pil(blended)
return blended
# # 读取图像
# image1 = cv2.imread('1.png')
# image2 = cv2.imread('3.png')
# # 进行正片叠底混合
# result = multiply_blend(image1, image2)
# cv2.imwrite('result.jpg', result)
def merge_images(bg_image, layer_image, mask, x, y, width, height, scale_option,is_multiply_blend=False):
# 打开底图
bg_image = bg_image.convert("RGBA")
@@ -682,8 +734,27 @@ def merge_images(bg_image, layer_image, mask, x, y, width, height, scale_option)
nw, nh = layer_image.size
mask = mask.resize((nw, nh))
# 在底图上粘贴图层
bg_image.paste(layer_image, (x, y), mask=mask)
# 分离出a通道
r, g, b, alpha = layer_image.split()
alpha = ImageOps.invert(alpha)
# 创建一个新的RGB图像
new_rgb_image = Image.new("RGB", layer_image.size)
# 将透明通道粘贴到新的RGB图像上
new_rgb_image.paste(layer_image, (0, 0), mask=alpha)
new_rgb_image.paste(layer_image, (x, y), mask=mask)
mask=new_rgb_image.convert('L')
mask = ImageOps.invert(mask)
if is_multiply_blend:
bg_image_white=Image.new("RGB", bg_image.size,(255, 255, 255))
bg_image_white.paste(layer_image, (x, y), mask=mask)
bg_image=multiply_blend(bg_image_white,bg_image)
bg_image=bg_image.convert("RGBA")
else:
# 在底图上粘贴图层
bg_image.paste(layer_image, (x, y), mask=mask)
# 输出合成后的图片
return bg_image
@@ -1753,7 +1824,7 @@ class NewLayer:
"display": "number" # Cosmetic only: display as "number" or "slider"
}),
"scale_option": (["width","height",'overall'],),
"image": ("IMAGE",),
"image": (any_type,),
},
"optional":{
"mask": ("MASK",{"default": None}),
@@ -1810,21 +1881,20 @@ def createMask(image,x,y,w,h):
# mask.save("mask.png")
return mask
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_width = int(width // num_cols)
grid_height = int(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
x = int(j * grid_width)
y = int(i * grid_height)
grid_coordinates.append((x, y, grid_width, grid_height))
return grid_coordinates
@@ -2156,12 +2226,16 @@ class GridOutput:
def INPUT_TYPES(s):
return {
"required": {
"grid": ("_GRID",)
}
"grid": ("_GRID",),
},
"optional":{
"bg_image":("IMAGE",)
}
}
RETURN_TYPES = ("INT","INT","INT","INT",)
RETURN_NAMES = ("x","y","width","height",)
RETURN_TYPES = ("INT","INT","INT","INT","MASK",)
RETURN_NAMES = ("x","y","width","height","mask",)
FUNCTION = "run"
@@ -2170,9 +2244,26 @@ class GridOutput:
INPUT_IS_LIST = False
# OUTPUT_IS_LIST = (True,)
def run(self,grid):
def run(self,grid,bg_image=None):
x,y,w,h=grid
return (x,y,w,h,)
x=int(x)
y=int(y)
w=int(w)
h=int(h)
masks=[]
if bg_image!=None:
for i in range(len(bg_image)):
im=bg_image[i]
#增加输出mask
im=tensor2pil(im)
mask=areaToMask(x,y,w,h,im)
mask=pil2tensor(mask)
masks.append(mask)
out=None
if len(masks)>0:
out = torch.cat(masks, dim=0)
return (x,y,w,h,out,)
@@ -2267,10 +2358,14 @@ class MergeLayers:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"layers": ("LAYER",),
"images": ("IMAGE",),
},
"layers": ("LAYER",),
"images": ("IMAGE",),
},
"optional":{
"is_multiply_blend": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("IMAGE","MASK",)
@@ -2283,11 +2378,12 @@ class MergeLayers:
INPUT_IS_LIST = True
# OUTPUT_IS_LIST = (False,)
def run(self,layers,images):
def run(self,layers,images,is_multiply_blend):
bg_images=[]
masks=[]
is_multiply_blend=is_multiply_blend[0]
# print(len(images),images[0].shape)
# 1 torch.Size([2, 512, 512, 3])
# 4 torch.Size([1, 1024, 768, 3])
@@ -2318,6 +2414,8 @@ class MergeLayers:
layer_image=tensor2pil(image)
layer_mask=tensor2pil(mask)
# t=layer_image.convert("RGBA")
# t.save('test.png') 如果layerimage传入的是rgba,则是透明的
bg_image=merge_images(bg_image,
layer_image,
layer_mask,
@@ -2325,7 +2423,8 @@ class MergeLayers:
layer['y'],
layer['width'],
layer['height'],
layer['scale_option']
layer['scale_option'],
is_multiply_blend
)
final_mask=merge_images(final_mask,
+32 -5
View File
@@ -2,16 +2,14 @@
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
import cv2,os
from nodes import MAX_RESOLUTION, SaveImage, common_ksampler
import folder_paths,random
# Tensor to PIL
def tensor2pil(image):
@@ -71,6 +69,35 @@ def combine(destination, source, x, y):
return output
class PreviewMask_(SaveImage):
def __init__(self):
self.output_dir = folder_paths.get_temp_directory()
self.type = "temp"
self.prefix_append =''.join(random.choice("abcdehijklmnopqrstupvxyzfg") for x in range(5))
self.compress_level = 4
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"mask": ("MASK",),
}
}
RETURN_TYPES = ()
OUTPUT_NODE = True
FUNCTION = "run"
CATEGORY = "♾️Mixlab/Mask"
# 运行的函数
def run(self, mask ):
img=tensor2pil(mask)
img=img.convert('RGB')
img=pil2tensor(img)
return self.save_images(img, 'temp_', None, None)
class OutlineMask:
@classmethod
+8 -2
View File
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
const version = 'v0.22.0'
const version = 'v0.22.1'
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
.then(response => response.json())
@@ -17,7 +17,13 @@ fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
return
if (latestVersion && latestVersion != version) {
localStorage.setItem('_mixlab_nodes_vesion', latestVersion)
app.ui.dialog.show(`<h4 style="font-size: 18px;">${repoName} <br>
app.ui.dialog.show(`<a style="color: white;
font-size: 18px;
font-weight: 800;
letter-spacing: 2px;
}"
href="https://discord.gg/cXs9vZSqeK">Welcome to Mixlab nodes discord</a>
<h4 style="font-size: 18px;">${repoName} <br>
Latest release version: ${latestVersion}</h4>
<p>Please proceed to the official repository to download the latest version.</p>
<a style="color: #2196F3;