commit ImageReel,ImageReelComposit,CheckMaskV2 nodes
This commit is contained in:
@@ -102,6 +102,8 @@ When this error has occurred, please check the network environment.
|
||||
## Update
|
||||
<font size="4">**If the dependency package error after updating, please reinstall the relevant dependency packages. </font><br />
|
||||
|
||||
* Commit [CheckMaskV2](#CheckMaskV2) node, Added the ```simple``` method to detect masks more quickly.
|
||||
* Commit [ImageReel](#ImageReel) and [ImageReelComposite](#ImageReelComposite) nodes to composite multiple images on a canvas.
|
||||
* [NumberCalculatorV2](#NumberCalculatorV2) and [NumberCalculator](#NumberCalculator) add the ```min``` and ```max``` method.
|
||||
* Optimize node loading speed.
|
||||
* [Florence2Image2Prompt](#Florence2Image2Prompt) add support for ```thwri/CogFlorence-2-Large-Freeze``` and ```thwri/CogFlorence-2.1-Large``` models. Please download the model files from [BaiduNetdisk](https://pan.baidu.com/s/1hzw9-QiU1vB8pMbBgofZIA?pwd=mfl3) or [huggingface/CogFlorence-2-Large-Freeze](https://huggingface.co/thwri/CogFlorence-2-Large-Freeze/tree/main) and [huggingface/CogFlorence-2.1-Large](https://huggingface.co/thwri/CogFlorence-2.1-Large/tree/main) , then copy it to ```ComfyUI/models/florence2``` folder.
|
||||
@@ -809,6 +811,44 @@ Node options:
|
||||
* opacity: Opacity of blend.
|
||||
* [note](#notes)
|
||||
|
||||
|
||||
### <a id="table1">ImageReel</a>
|
||||
Display multiple images in one reel. Text annotations can be added to each image in the reel. By using the [ImageReelComposite](#ImageReelComposite) node, multiple reel can be combined into one image.
|
||||

|
||||
|
||||
Node Options:
|
||||

|
||||
* image1: The first image. it must be input.
|
||||
* image2: The second image. optional input.
|
||||
* image3: The third image. optional input.
|
||||
* image4: The fourth image. optional input.
|
||||
* image1_text: Text annotation for the first image.
|
||||
* image2_text: Text annotation for the second image.
|
||||
* image3_text: Text annotation for the third image.
|
||||
* image4_text: Text annotation for the fourth image.
|
||||
* reel_height: The height of reel.
|
||||
* border: The border width of the image in the reel.
|
||||
|
||||
Output:
|
||||
* reel: The reel of [ImageReelComposite](#ImageReelComposit) node input.
|
||||
|
||||
### <a id="table1">ImageReelComposite</a>
|
||||
Combine multiple reel into one image.
|
||||
|
||||
Node Options:
|
||||

|
||||
* reel_1: The first reel. it must be input.
|
||||
* reel_2: The second reel. optional input.
|
||||
* reel_3: The third reel. optional input.
|
||||
* reel_4: The fourth reel. optional input.
|
||||
* font_file<sup>**</sup>: Here is a list of available font files in the font folder, and the selected font files will be used to generate images.
|
||||
* border: The border width of the reel.
|
||||
* color_theme: Theme color for the output image.
|
||||
<sup>*</sup>The font folder is defined in ```resource_dir.ini```, this file is located in the root directory of the plug-in, and the default name is ```resource_dir.ini.example```. to use this file for the first time, you need to change the file suffix to ```.ini```.
|
||||
Open the text editing software and find the line starting with "FONT_dir=", after "=", enter the custom folder path name. all font files in this folder will be collected and displayed in the node list during ComfyUI initialization.
|
||||
If the folder set in ini is invalid, the font folder that comes with the plugin will be enabled.
|
||||
|
||||
|
||||
### <a id="table1">ImageOpacity</a>
|
||||
Adjust image opacity
|
||||

|
||||
@@ -1255,6 +1295,12 @@ Node Options:
|
||||
* white_point: The white point threshold used to determine whether the mask is valid is considered valid if it exceeds this value.
|
||||
* area_percent: The percentage of effective areas. If the proportion of effective areas exceeds this value, output True.
|
||||
|
||||
### <a id="table1">CheckMaskV2</a>
|
||||
On the basis of CheckMask, the ```method``` option has been added, which allows for the selection of different detection methods. The ```area_percent``` is changed to a floating point number with an accuracy of 2 decimal places, which can detect smaller effective areas.
|
||||
|
||||
Node Options:
|
||||

|
||||
* method: There are two detection methods, which are ```simple``` and ```detectability```. The simple method only detects whether the mask is completely black, while the detect_percent method detects the proportion of effective areas.
|
||||
|
||||
### <a id="table1">If</a>
|
||||

|
||||
|
||||
@@ -103,6 +103,8 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
|
||||
## 更新说明
|
||||
<font size="4">**如果本插件更新后出现依赖包错误,请重新安装相关依赖包。
|
||||
|
||||
* 添加 [CheckMaskV2](#CheckMaskV2) 节点,增加了```simple```方法以更快速检测遮罩。
|
||||
* 添加 [ImageReel ](#ImageReel) 和 [ImageReelComposit](#ImageReelComposit) 节点,可将多张图片显示在一起。
|
||||
* [NumberCalculatorV2](#NumberCalculatorV2) 和 [NumberCalculator](#NumberCalculator) 节点增加 ```min``` 和 ```max``` 方法。
|
||||
* 优化节点加载速度。
|
||||
* [Florence2Image2Prompt](#Florence2Image2Prompt) 增加thwri/CogFlorence-2-Large-Freeze 和 thwri/CogFlorence-2.1-Large 模型支持。请从[百度网盘](https://pan.baidu.com/s/1hzw9-QiU1vB8pMbBgofZIA?pwd=mfl3) 或 [huggingface/CogFlorence-2-Large-Freeze](https://huggingface.co/thwri/CogFlorence-2-Large-Freeze/tree/main) 和 [huggingface/CogFlorence-2.1-Large](https://huggingface.co/thwri/CogFlorence-2.1-Large/tree/main) 下载模型文件并复制到```ComfyUI/models/florence2```文件夹。
|
||||
@@ -799,6 +801,40 @@ ImageScaleByAspectRatio的V2升级版
|
||||
* opacity: 不透明度。
|
||||
* [节点注解](#节点注解)
|
||||
|
||||
### <a id="table1">ImageReel</a>
|
||||
将多张图片显示在一个卷轴中。可为卷轴中的每张图片添加文字注解。配合[ImageReelComposite](#ImageReelComposit)节点可将多个卷轴拼合为一张图片。
|
||||

|
||||
|
||||
节点选项说明:
|
||||

|
||||
* image1: 第一张图片。必须输入。
|
||||
* image2: 第二张图片。可选输入图片。
|
||||
* image3: 第三张图片。可选输入图片。
|
||||
* image4: 第四张图片。可选输入图片。
|
||||
* image1_text: 第一张图片的文字注解。
|
||||
* image2_text: 第二张图片的文字注解。
|
||||
* image3_text: 第三张图片的文字注解。
|
||||
* image4_text: 第四张图片的文字注解。
|
||||
* reel_height: 卷轴高度。
|
||||
* border: 卷轴中图片的边框宽度。
|
||||
|
||||
输出:
|
||||
* reel:卷轴,用于输入[ImageReelComposite](#ImageReelComposit)节点。
|
||||
|
||||
### <a id="table1">ImageReelComposite</a>
|
||||
将多个卷轴拼合为一张图片。
|
||||
|
||||
节点选项说明:
|
||||

|
||||
* reel_1: 第一个卷轴。必须输入。
|
||||
* reel_2: 第二个卷轴。可选输入。
|
||||
* reel_3: 第三个卷轴。可选输入。
|
||||
* reel_4: 第四个卷轴。可选输入。
|
||||
* font_file<sup>*</sup>: 字体文件。
|
||||
* border: 卷轴的边框宽度。
|
||||
* color_theme: 主题色。
|
||||
<sup>*</sup>font文件夹在```resource_dir.ini```中定义,这个文件位于插件根目录下, 默认名字是```resource_dir.ini.example```, 初次使用这个文件需将文件后缀改为.ini。用文本编辑软件打开,找到“FONT_dir=”开头的这一行,编辑“=”之后为自定义文件夹路径名。这个文件夹里面所有的.ttf和.otf文件将在ComfyUI初始化时被收集并显示在节点的列表中。如果ini中设定的文件夹无效,将启用插件自带的font文件夹。
|
||||
|
||||
### <a id="table1">ImageOpacity</a>
|
||||
调整图像不透明度。
|
||||

|
||||
@@ -1242,6 +1278,13 @@ BooleanOperator的升级版,增加了节点内数值输入,增加了大于
|
||||
* white_point: 判断遮罩是否有效的白点值,高于此值被计入有效。
|
||||
* area_percent: 有效区域所占百分比。检测有效区域占比超过此值则输出True。
|
||||
|
||||
### <a id="table1">CheckMaskV2</a>
|
||||
在CheckMask基础上增加了```method```选项,可以选择不同的检测方法。area_percent改为浮点数,精度为小数点后2位,可检测更小的有效区域。
|
||||
|
||||
节点选项说明:
|
||||

|
||||
* method: 检测方法,有```simple``` 和 ```detect_percent``` 两种。simple方法仅检测mask是否全黑,detect_percent方法检测有效区域占比。
|
||||
|
||||
### <a id="table1">If</a>
|
||||

|
||||
根据布尔值条件输入切换输出。可用于任意类型的数据切换,包括且不限于数值、字符串、图片、遮罩、模型、latent、pipe管线等。
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 327 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -0,0 +1,56 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'CheckMaskV2'
|
||||
|
||||
# 检查mask是否有效,如果mask面积少于指定比例则判为无效mask
|
||||
class CheckMaskV2:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
method_list = ['simple', 'detect_percent']
|
||||
blank_mask_list = ['white', 'black']
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK",), #
|
||||
"method": (method_list,), #
|
||||
"white_point": ("INT", {"default": 1, "min": 1, "max": 254, "step": 1}), # 用于判断mask是否有效的白点值,高于此值被计入有效
|
||||
"area_percent": ("FLOAT", {"default": 0.01, "min": 0, "max": 100, "step": 0.01}), # 区域百分比,低于此则mask判定无效
|
||||
},
|
||||
"optional": { #
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN",)
|
||||
RETURN_NAMES = ('bool',)
|
||||
FUNCTION = 'check_mask_v2'
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
|
||||
def check_mask_v2(self, mask, method, white_point, area_percent,):
|
||||
|
||||
if mask.dim() == 2:
|
||||
mask = torch.unsqueeze(mask, 0)
|
||||
tensor_mask = mask[0]
|
||||
print(f"tensor_mask={tensor_mask},shape is {tensor_mask.shape}")
|
||||
pil_mask = tensor2pil(tensor_mask)
|
||||
if pil_mask.width * pil_mask.height > 262144:
|
||||
target_width = 512
|
||||
target_height = int(target_width * pil_mask.height / pil_mask.width)
|
||||
pil_mask = pil_mask.resize((target_width, target_height), Image.LANCZOS)
|
||||
ret_bool = False
|
||||
if method == 'simple':
|
||||
ret_bool = is_valid_mask(tensor_mask)
|
||||
else:
|
||||
ret_bool = mask_white_area(pil_mask, white_point) * 100 > area_percent
|
||||
|
||||
return (ret_bool,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: CheckMaskV2": CheckMaskV2
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: CheckMaskV2": "LayerUtility: Check Mask V2"
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class ImageReelPipeline:
|
||||
def __init__(self):
|
||||
self.image = None
|
||||
self.texts = {}
|
||||
self.reel_height = 0
|
||||
self.reel_border = 0
|
||||
|
||||
Reel = ImageReelPipeline()
|
||||
class ImageReel:
|
||||
|
||||
def __init__(self):
|
||||
self.NODE_NAME = 'ImageReel'
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"image1": ("IMAGE",),
|
||||
"image1_text": ("STRING", {"multiline": False, "default": "image1"}),
|
||||
"image2_text": ("STRING", {"multiline": False, "default": "image2"}),
|
||||
"image3_text": ("STRING", {"multiline": False, "default": "image3"}),
|
||||
"image4_text": ("STRING", {"multiline": False, "default": "image4"}),
|
||||
"reel_height": ("INT", {"default": 512, "min": 64, "max": 2048}),
|
||||
"border": ("INT", {"default": 32, "min": 8, "max": 512}),
|
||||
},
|
||||
"optional": {
|
||||
"image2": ("IMAGE",),
|
||||
"image3": ("IMAGE",),
|
||||
"image4": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("Reel",)
|
||||
RETURN_NAMES = ("reel",)
|
||||
FUNCTION = 'image_reel'
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
|
||||
def image_reel(self, image1, image1_text, image2_text, image3_text, image4_text,
|
||||
reel_height, border,
|
||||
image2=None, image3=None, image4=None,):
|
||||
|
||||
image_list = []
|
||||
texts = []
|
||||
for img in image1:
|
||||
i = self.resize_image_to_height(tensor2pil(img.unsqueeze(0)),reel_height)
|
||||
image_list.append(i)
|
||||
texts.append([image1_text,i.width])
|
||||
if image2 is not None:
|
||||
for img in image2:
|
||||
i = self.resize_image_to_height(tensor2pil(img.unsqueeze(0)),reel_height)
|
||||
image_list.append(i)
|
||||
texts.append([image2_text,i.width])
|
||||
if image3 is not None:
|
||||
for img in image3:
|
||||
i = self.resize_image_to_height(tensor2pil(img.unsqueeze(0)),reel_height)
|
||||
image_list.append(i)
|
||||
texts.append([image3_text,i.width])
|
||||
if image4 is not None:
|
||||
for img in image4:
|
||||
i = self.resize_image_to_height(tensor2pil(img.unsqueeze(0)),reel_height)
|
||||
image_list.append(i)
|
||||
texts.append([image4_text,i.width])
|
||||
|
||||
reel = ImageReel()
|
||||
reel.image = self.draw_reel_image(image_list, border, reel_height)
|
||||
reel.texts = texts
|
||||
reel.reel_height = reel_height
|
||||
reel.reel_border = border
|
||||
return (reel,)
|
||||
|
||||
def resize_image_to_height(self, image, target_height) -> Image:
|
||||
w = int(target_height / image.height * image.width)
|
||||
return image.resize((w, target_height), Image.LANCZOS)
|
||||
|
||||
def draw_reel_image(self, image_list, border, reel_height) -> Image:
|
||||
reel_width = 0
|
||||
for img in image_list:
|
||||
reel_width += img.width + border
|
||||
reel_img = Image.new('RGBA', (reel_width, reel_height + border), color=(0, 0, 0, 0))
|
||||
#paste images
|
||||
w = border // 2
|
||||
for img in image_list:
|
||||
reel_img.paste(img, (w, border // 2))
|
||||
w += img.width + border
|
||||
return reel_img
|
||||
|
||||
|
||||
class ImageReelComposit:
|
||||
|
||||
def __init__(self):
|
||||
self.NODE_NAME = 'ImageReelComposit'
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
color_theme_list = ['light', 'dark']
|
||||
return {
|
||||
"required": {
|
||||
"reel_1": ("Reel",),
|
||||
"font_file": (FONT_LIST,),
|
||||
"font_size": ("INT", {"default": 40, "min": 4, "max": 1024}),
|
||||
"border": ("INT", {"default": 32, "min": 8, "max": 512}),
|
||||
"color_theme": (color_theme_list,),
|
||||
},
|
||||
"optional": {
|
||||
"reel_2": ("Reel",),
|
||||
"reel_3": ("Reel",),
|
||||
"reel_4": ("Reel",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image1",)
|
||||
FUNCTION = 'image_reel_composit'
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
|
||||
def image_reel_composit(self, reel_1, font_file, font_size, border, color_theme, reel_2=None, reel_3=None, reel_4=None,):
|
||||
|
||||
ret_images = []
|
||||
|
||||
if color_theme == 'light':
|
||||
bg_color = "#E5E5E5"
|
||||
text_color = "#121212"
|
||||
else:
|
||||
bg_color = "#121212"
|
||||
text_color = "#E5E5E5"
|
||||
|
||||
|
||||
font_space = int(font_size * 1.5)
|
||||
width = reel_1.image.width
|
||||
height = reel_1.image.height + font_space + border
|
||||
if reel_2 is not None:
|
||||
width = max(width, reel_2.image.width)
|
||||
height += reel_2.image.height + font_space + border
|
||||
if reel_3 is not None:
|
||||
width = max(width, reel_3.image.width)
|
||||
height += reel_3.image.height + font_space + border
|
||||
if reel_4 is not None:
|
||||
width = max(width, reel_4.image.width)
|
||||
height += reel_4.image.height + font_space + border
|
||||
|
||||
ret_image = Image.new('RGB', (width, height), color=bg_color)
|
||||
paste_y = 0
|
||||
reel1_text_image = self.draw_reel_text(reel_1, font_file, font_size, text_color)
|
||||
shadow_size = reel_1.image.height // 80
|
||||
ret_image = self.paste_drop_shadow(ret_image, reel_1.image, reel1_text_image, ((width - reel_1.image.width) // 2, paste_y),
|
||||
shadow_size, text_color)
|
||||
|
||||
paste_y += reel_1.image.height + font_space + border
|
||||
if reel_2 is not None:
|
||||
reel2_text_image = self.draw_reel_text(reel_2, font_file, font_size, text_color)
|
||||
shadow_size = reel_2.image.height // 80
|
||||
ret_image = self.paste_drop_shadow(ret_image, reel_2.image, reel2_text_image, ((width - reel_2.image.width) // 2, paste_y),
|
||||
shadow_size, text_color)
|
||||
paste_y += reel_2.image.height + font_space + border
|
||||
if reel_3 is not None:
|
||||
reel3_text_image = self.draw_reel_text(reel_3, font_file, font_size, text_color)
|
||||
shadow_size = reel_3.image.height // 80
|
||||
ret_image = self.paste_drop_shadow(ret_image, reel_3.image, reel3_text_image,((width - reel_3.image.width) // 2, paste_y),
|
||||
shadow_size, text_color)
|
||||
paste_y += reel_3.image.height + font_space + border
|
||||
if reel_4 is not None:
|
||||
reel4_text_image = self.draw_reel_text(reel_4, font_file, font_size, text_color)
|
||||
shadow_size = reel_4.image.height // 80
|
||||
ret_image = self.paste_drop_shadow(ret_image, reel_4.image, reel4_text_image,((width - reel_4.image.width) // 2, paste_y),
|
||||
shadow_size, text_color)
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f"{self.NODE_NAME} Processed {len(ret_images)} image(s).", message_type='finish')
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
def paste_drop_shadow(self, background_image, image, text_image, box, shadow_size, text_color) -> Image:
|
||||
# drop shadow
|
||||
_mask = image.split()[3]
|
||||
_blured_mask = gaussian_blur(_mask, shadow_size//1.3)
|
||||
_blured_mask = adjust_levels(_blured_mask, 0, 255, 0.5, 0, output_white=54).convert('L')
|
||||
background_image.paste(Image.new('RGBA', image.size, color="black"), (box[0]+shadow_size, box[1]+shadow_size), mask=_blured_mask)
|
||||
background_image.paste(image, box, mask=_mask)
|
||||
background_image.paste(Image.new('RGB', text_image.size, color=text_color), (box[0], box[1] + image.height), mask=text_image.split()[3])
|
||||
return background_image
|
||||
|
||||
def draw_reel_text(self, reel, font_file, font_size, text_color) -> Image:
|
||||
font_path = FONT_DICT.get(font_file)
|
||||
font = ImageFont.truetype(font_path, font_size)
|
||||
texts = reel.texts
|
||||
text_image = Image.new('RGBA', (reel.image.width, reel.reel_border + int(font_size * 1.5)), color=(0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(text_image)
|
||||
x = reel.reel_border
|
||||
for t in texts:
|
||||
text = t[0]
|
||||
width = t[1]
|
||||
text_width = font.getbbox(text)[2]
|
||||
draw.text(
|
||||
xy=(x + width // 2 - text_width//2, reel.reel_border//4),
|
||||
text=text,
|
||||
fill=text_color,
|
||||
font=font,
|
||||
)
|
||||
x += width + reel.reel_border
|
||||
return text_image
|
||||
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: ImageReel": ImageReel,
|
||||
"LayerUtility: ImageReelComposit": ImageReelComposit
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: ImageReel": "LayerUtility: Image Reel",
|
||||
"LayerUtility: ImageReelComposit": "LayerUtility: Image Reel Composit"
|
||||
}
|
||||
@@ -63,17 +63,23 @@ class ImageScaleByAspectRatioV2:
|
||||
mask = torch.unsqueeze(mask, 0)
|
||||
for m in mask:
|
||||
m = torch.unsqueeze(m, 0)
|
||||
orig_masks.append(m)
|
||||
_width, _height = tensor2pil(orig_masks[0]).size
|
||||
if (orig_width > 0 and orig_width != _width) or (orig_height > 0 and orig_height != _height):
|
||||
log(f"Error: {NODE_NAME} skipped, because the mask is does'nt match image.", message_type='error')
|
||||
return (None, None, None, 0, 0,)
|
||||
elif orig_width + orig_height == 0:
|
||||
orig_width = _width
|
||||
orig_height = _height
|
||||
print(f"m.shape={m.shape}")
|
||||
if not is_valid_mask(m) and m.shape==torch.Size([1,64,64]):
|
||||
log(f"Warning: {NODE_NAME} input mask is empty, ignore it.", message_type='warning')
|
||||
else:
|
||||
orig_masks.append(m)
|
||||
|
||||
if len(orig_masks) > 0:
|
||||
_width, _height = tensor2pil(orig_masks[0]).size
|
||||
if (orig_width > 0 and orig_width != _width) or (orig_height > 0 and orig_height != _height):
|
||||
log(f"Error: {NODE_NAME} execute failed, because the mask is does'nt match image.", message_type='error')
|
||||
return (None, None, None, 0, 0,)
|
||||
elif orig_width + orig_height == 0:
|
||||
orig_width = _width
|
||||
orig_height = _height
|
||||
|
||||
if orig_width + orig_height == 0:
|
||||
log(f"Error: {NODE_NAME} skipped, because the image or mask at least one must be input.", message_type='error')
|
||||
log(f"Error: {NODE_NAME} execute failed, because the image or mask at least one must be input.", message_type='error')
|
||||
return (None, None, None, 0, 0,)
|
||||
|
||||
if aspect_ratio == 'original':
|
||||
|
||||
@@ -1813,6 +1813,8 @@ def HSV_255level_to_Hex(HSV: list) -> str:
|
||||
return '#' + hex_r + hex_g + hex_b
|
||||
|
||||
'''Value Functions'''
|
||||
def is_valid_mask(tensor:torch.Tensor) -> bool:
|
||||
return not bool(torch.all(tensor == 0).item())
|
||||
|
||||
def step_value(start_value, end_value, total_step, step) -> float: # 按当前步数在总步数中的位置返回比例值
|
||||
factor = step / total_step
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui_layerstyle"
|
||||
description = "A set of nodes for ComfyUI it generate image like Adobe Photoshop's Layer Style. the Drop Shadow is first completed node, and follow-up work is in progress."
|
||||
version = "1.0.27"
|
||||
version = "1.0.28"
|
||||
license = "MIT"
|
||||
dependencies = ["numpy", "pillow", "torch", "matplotlib", "Scipy", "scikit_image", "opencv-contrib-python", "pymatting", "segment_anything", "timm", "addict", "yapf", "colour-science", "wget", "mediapipe", "loguru", "typer_config", "fastapi", "rich", "google-generativeai", "diffusers", "omegaconf", "tqdm", "transformers", "kornia", "image-reward", "ultralytics", "blend_modes", "blind-watermark", "qrcode", "pyzbar", "transparent-background", "huggingface_hub", "psd-tools"]
|
||||
|
||||
|
||||
@@ -0,0 +1,477 @@
|
||||
{
|
||||
"last_node_id": 9,
|
||||
"last_link_id": 8,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 3,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
290,
|
||||
340
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
314
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
2
|
||||
],
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"1280x720car.jpg",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
290,
|
||||
710
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
314.0000114440918
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
3
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"1344x768_redcar.png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
990,
|
||||
650
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
5
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"1344x768_hair.png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
980,
|
||||
1020
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
8
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"768x1344_dress.png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"type": "LayerUtility: ImageReel",
|
||||
"pos": [
|
||||
650,
|
||||
590
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 238
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image1",
|
||||
"type": "IMAGE",
|
||||
"link": 2
|
||||
},
|
||||
{
|
||||
"name": "image2",
|
||||
"type": "IMAGE",
|
||||
"link": 3
|
||||
},
|
||||
{
|
||||
"name": "image3",
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "image4",
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "reel",
|
||||
"type": "Reel",
|
||||
"links": [
|
||||
1
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerUtility: ImageReel"
|
||||
},
|
||||
"widgets_values": [
|
||||
"image1 on reel1",
|
||||
"image2 on reel1",
|
||||
"image3",
|
||||
"image4",
|
||||
768,
|
||||
32
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "LayerUtility: ImageReel",
|
||||
"pos": [
|
||||
1350,
|
||||
750
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 238
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image1",
|
||||
"type": "IMAGE",
|
||||
"link": 4
|
||||
},
|
||||
{
|
||||
"name": "image2",
|
||||
"type": "IMAGE",
|
||||
"link": 5
|
||||
},
|
||||
{
|
||||
"name": "image3",
|
||||
"type": "IMAGE",
|
||||
"link": 8
|
||||
},
|
||||
{
|
||||
"name": "image4",
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "reel",
|
||||
"type": "Reel",
|
||||
"links": [
|
||||
6
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerUtility: ImageReel"
|
||||
},
|
||||
"widgets_values": [
|
||||
"image1 on reel2",
|
||||
"image2 on reel2",
|
||||
"image3 on reel2",
|
||||
"image4",
|
||||
1024,
|
||||
32
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LayerUtility: ImageReelComposit",
|
||||
"pos": [
|
||||
1690,
|
||||
590
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 190
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "reel_1",
|
||||
"type": "Reel",
|
||||
"link": 1
|
||||
},
|
||||
{
|
||||
"name": "reel_2",
|
||||
"type": "Reel",
|
||||
"link": 6
|
||||
},
|
||||
{
|
||||
"name": "reel_3",
|
||||
"type": "Reel",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "reel_4",
|
||||
"type": "Reel",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image1",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
7
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerUtility: ImageReelComposit"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Alibaba-PuHuiTi-Heavy.ttf",
|
||||
40,
|
||||
32,
|
||||
"light"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
2060,
|
||||
380
|
||||
],
|
||||
"size": [
|
||||
1023.9856266119964,
|
||||
713.8515867095819
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 7
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
990,
|
||||
280
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
4
|
||||
],
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"768x1344_beach.png",
|
||||
"image"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
"Reel"
|
||||
],
|
||||
[
|
||||
2,
|
||||
3,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
3,
|
||||
4,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
4,
|
||||
6,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
5,
|
||||
7,
|
||||
0,
|
||||
5,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
6,
|
||||
5,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
"Reel"
|
||||
],
|
||||
[
|
||||
7,
|
||||
2,
|
||||
0,
|
||||
9,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
8,
|
||||
8,
|
||||
0,
|
||||
5,
|
||||
2,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.6209213230591553,
|
||||
"offset": [
|
||||
304.4138606804165,
|
||||
388.12270422110066
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
Reference in New Issue
Block a user