diff --git a/README.MD b/README.MD
index 43d8140..21c76fd 100644
--- a/README.MD
+++ b/README.MD
@@ -102,6 +102,8 @@ When this error has occurred, please check the network environment.
## Update
**If the dependency package error after updating, please reinstall the relevant dependency packages.
+* 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)
+
+### ImageReel
+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.
+
+### ImageReelComposite
+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**: 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.
+*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.
+
+
### ImageOpacity
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.
+### CheckMaskV2
+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.
### If

diff --git a/README_CN.MD b/README_CN.MD
index 5ef9aa7..4151ee3 100644
--- a/README_CN.MD
+++ b/README_CN.MD
@@ -103,6 +103,8 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
## 更新说明
**如果本插件更新后出现依赖包错误,请重新安装相关依赖包。
+* 添加 [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: 不透明度。
* [节点注解](#节点注解)
+### ImageReel
+将多张图片显示在一个卷轴中。可为卷轴中的每张图片添加文字注解。配合[ImageReelComposite](#ImageReelComposit)节点可将多个卷轴拼合为一张图片。
+
+
+节点选项说明:
+
+* image1: 第一张图片。必须输入。
+* image2: 第二张图片。可选输入图片。
+* image3: 第三张图片。可选输入图片。
+* image4: 第四张图片。可选输入图片。
+* image1_text: 第一张图片的文字注解。
+* image2_text: 第二张图片的文字注解。
+* image3_text: 第三张图片的文字注解。
+* image4_text: 第四张图片的文字注解。
+* reel_height: 卷轴高度。
+* border: 卷轴中图片的边框宽度。
+
+输出:
+* reel:卷轴,用于输入[ImageReelComposite](#ImageReelComposit)节点。
+
+### ImageReelComposite
+将多个卷轴拼合为一张图片。
+
+节点选项说明:
+
+* reel_1: 第一个卷轴。必须输入。
+* reel_2: 第二个卷轴。可选输入。
+* reel_3: 第三个卷轴。可选输入。
+* reel_4: 第四个卷轴。可选输入。
+* font_file*: 字体文件。
+* border: 卷轴的边框宽度。
+* color_theme: 主题色。
+*font文件夹在```resource_dir.ini```中定义,这个文件位于插件根目录下, 默认名字是```resource_dir.ini.example```, 初次使用这个文件需将文件后缀改为.ini。用文本编辑软件打开,找到“FONT_dir=”开头的这一行,编辑“=”之后为自定义文件夹路径名。这个文件夹里面所有的.ttf和.otf文件将在ComfyUI初始化时被收集并显示在节点的列表中。如果ini中设定的文件夹无效,将启用插件自带的font文件夹。
+
### ImageOpacity
调整图像不透明度。

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

根据布尔值条件输入切换输出。可用于任意类型的数据切换,包括且不限于数值、字符串、图片、遮罩、模型、latent、pipe管线等。
diff --git a/image/check_mask_v2_node.jpg b/image/check_mask_v2_node.jpg
new file mode 100644
index 0000000..be3a091
Binary files /dev/null and b/image/check_mask_v2_node.jpg differ
diff --git a/image/image_reel_composit_node.jpg b/image/image_reel_composit_node.jpg
new file mode 100644
index 0000000..78f37ee
Binary files /dev/null and b/image/image_reel_composit_node.jpg differ
diff --git a/image/image_reel_example.jpg b/image/image_reel_example.jpg
new file mode 100644
index 0000000..a27162f
Binary files /dev/null and b/image/image_reel_example.jpg differ
diff --git a/image/image_reel_node.jpg b/image/image_reel_node.jpg
new file mode 100644
index 0000000..c96c12d
Binary files /dev/null and b/image/image_reel_node.jpg differ
diff --git a/py/check_mask_v2.py b/py/check_mask_v2.py
new file mode 100644
index 0000000..cbd0e29
--- /dev/null
+++ b/py/check_mask_v2.py
@@ -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"
+}
\ No newline at end of file
diff --git a/py/image_reel.py b/py/image_reel.py
new file mode 100644
index 0000000..708b1e5
--- /dev/null
+++ b/py/image_reel.py
@@ -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"
+}
\ No newline at end of file
diff --git a/py/image_scale_by_aspect_ratio_v2.py b/py/image_scale_by_aspect_ratio_v2.py
index 9da5751..0b2b5bf 100644
--- a/py/image_scale_by_aspect_ratio_v2.py
+++ b/py/image_scale_by_aspect_ratio_v2.py
@@ -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':
diff --git a/py/imagefunc.py b/py/imagefunc.py
index d3affc6..6c1f0cc 100644
--- a/py/imagefunc.py
+++ b/py/imagefunc.py
@@ -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
diff --git a/pyproject.toml b/pyproject.toml
index 4087a73..4a776ff 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -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"]
diff --git a/workflow/image_reel_example.json b/workflow/image_reel_example.json
new file mode 100644
index 0000000..e8dcb62
--- /dev/null
+++ b/workflow/image_reel_example.json
@@ -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
+}
\ No newline at end of file