commit ImageReel,ImageReelComposit,CheckMaskV2 nodes

This commit is contained in:
chflame163
2024-08-24 17:22:48 +08:00
parent 8821883f63
commit 18e21036ef
12 changed files with 854 additions and 10 deletions
+46
View File
@@ -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.
![image](image/image_reel_example.jpg)
Node Options:
![image](image/image_reel_node.jpg)
* 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:
![image](image/image_reel_composit_node.jpg)
* 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
![image](image/image_opacity_example.jpg)
@@ -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:
![image](image/check_mask_v2_node.jpg)
* 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>
![image](image/if_example.jpg)
+43
View File
@@ -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)节点可将多个卷轴拼合为一张图片。
![image](image/image_reel_example.jpg)
节点选项说明:
![image](image/image_reel_node.jpg)
* image1: 第一张图片。必须输入。
* image2: 第二张图片。可选输入图片。
* image3: 第三张图片。可选输入图片。
* image4: 第四张图片。可选输入图片。
* image1_text: 第一张图片的文字注解。
* image2_text: 第二张图片的文字注解。
* image3_text: 第三张图片的文字注解。
* image4_text: 第四张图片的文字注解。
* reel_height: 卷轴高度。
* border: 卷轴中图片的边框宽度。
输出:
* reel:卷轴,用于输入[ImageReelComposite](#ImageReelComposit)节点。
### <a id="table1">ImageReelComposite</a>
将多个卷轴拼合为一张图片。
节点选项说明:
![image](image/image_reel_composit_node.jpg)
* 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>
调整图像不透明度。
![image](image/image_opacity_example.jpg)
@@ -1242,6 +1278,13 @@ BooleanOperator的升级版,增加了节点内数值输入,增加了大于
* white_point: 判断遮罩是否有效的白点值,高于此值被计入有效。
* area_percent: 有效区域所占百分比。检测有效区域占比超过此值则输出True。
### <a id="table1">CheckMaskV2</a>
在CheckMask基础上增加了```method```选项,可以选择不同的检测方法。area_percent改为浮点数,精度为小数点后2位,可检测更小的有效区域。
节点选项说明:
![image](image/check_mask_v2_node.jpg)
* method: 检测方法,有```simple``` 和 ```detect_percent``` 两种。simple方法仅检测mask是否全黑,detect_percent方法检测有效区域占比。
### <a id="table1">If</a>
![image](image/if_example.jpg)
根据布尔值条件输入切换输出。可用于任意类型的数据切换,包括且不限于数值、字符串、图片、遮罩、模型、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

+56
View File
@@ -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"
}
+214
View File
@@ -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"
}
+15 -9
View File
@@ -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':
+2
View File
@@ -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
View File
@@ -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"]
+477
View File
@@ -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
}