commit LoadImagesFromPath and ImageTaggerSaveV2 nodes
This commit is contained in:
@@ -147,6 +147,8 @@ When this error has occurred, please check the network environment.
|
||||
|
||||
<font size="4">**If the dependency package error after updating, please double clicking ```repair_dependency.bat``` (for Official ComfyUI Protable) or ```repair_dependency_aki.bat``` (for ComfyUI-aki-v1.x) in the plugin folder to reinstall the dependency packages. </font><br />
|
||||
|
||||
* Commit [LoadImagesFromPath](#LoadImagesFromPath) and [ImageTaggerSaveV2](#ImageTaggerSaveV2) nodes, used to load a list of images from a folder and save images and tagger text file with corresponding file names.
|
||||
* Commit [LoadImageFromPath](LoadImageFromPath) node, The images in a folder can be loaded and output as image list, also supporting output of a list corresponding to a file name.
|
||||
* Commit [SegformerUltraV3](SegformerUltraV3), [LoadSegformerModel](LoadSegformerModel), [SegformerClothesSetting](SegformerClothesSetting) and [SegformerFashionSetting](SegformerFashionSetting) nodes, Separate the loading of models and settings to save resources when using multiple nodes.
|
||||
* Add multiple languages and increase support for 5 languages: Chinese, French, Japanese, Korean and Russian. This feature producted by [ComfyUI-Globalization-Node-Translation](https://github.com/yamanacn/ComfyUI-Globalization-Node-Translation), thank you to the original author.
|
||||
* Commit [HalfTone](#HalfTone) node, use for halftone processing of images.
|
||||
@@ -1744,6 +1746,28 @@ Node Options:
|
||||
|
||||
<sup>*</sup> Enter```%date``` for the current date (YY-mm-dd) and ```%time``` for the current time (HH-MM-SS). You can enter ```/``` for subdirectories. For example, ```%date/name_%tiem``` will output the image to the ```YY-mm-dd``` folder, with ```name_HH-MM-SS``` as the file name prefix.
|
||||
|
||||
### <a id="table1">ImageTaggerSaveV2</a>
|
||||
The upgraded version of [ImageTaggerSave](#ImageTaggerSave) node can be used in conjunction with [LoadImagesFromPath](#LoadImagesFromPath) node to save the text label files of corresponding images in the folder, while maintaining the original file name.
|
||||
|
||||
The following options have been added to the original node:
|
||||

|
||||
* custom_filename: User defined file name. If there is input here, use it as the save file name; otherwise, use filename_prefix as the file name prefix.
|
||||
* remove_custom_filename_ext: Whether to remove the extension from the original file name.
|
||||
|
||||
### <a id="table1">LoadImagesFromPath</a>
|
||||
Load images from the specified folder.
|
||||
|
||||
Node Options:
|
||||

|
||||
* path: Folder path.
|
||||
* image_load_cap: The number of output files. The default value of 0 means to read all image files in the folder.
|
||||
* select_every_nth: Loads one image every ```select_every_nth``` images, skipping others.
|
||||
|
||||
Outputs:
|
||||
* images: Output image list.
|
||||
* masks: Output the mask list corresponding to the image.
|
||||
* file_name: Output a list of file names corresponding to the images.
|
||||
* frame_count: Output the total number of images.
|
||||
|
||||
# <a id="table1">LayerMask</a>
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
|
||||
## 更新说明
|
||||
<font size="4">**如果本插件更新后出现依赖包错误,请双击运行插件目录下的```install_requirements.bat```(官方便携包),或 ```install_requirements_aki.bat```(秋叶整合包) 重新安装依赖包。
|
||||
|
||||
* 添加 [LoadImagesFromPath](#LoadImagesFromPath) 和 [ImageTaggerSaveV2](#ImageTaggerSaveV2) 节点,用于从文件夹加载图片列表并保存对应文件名的图片和标签。
|
||||
* 添加 [SegformerUltraV3](SegformerUltraV3), [LoadSegformerModel](LoadSegformerModel), [SegformerClothesSetting](SegformerClothesSetting) 和 [SegformerFashionSetting](SegformerFashionSetting) 节点,将模型与设置分离加载,在使用多个节点时节省资源。
|
||||
* 添加多语言,除英语外,增加支持5种语言:中文、法语、日语、韩语和俄语。该功能使用[ComfyUI-Globalization-Node-Translation](https://github.com/yamanacn/ComfyUI-Globalization-Node-Translation)制作,感谢原作者。
|
||||
* 添加 [HalfTone](#HalfTone) 节点,用于对图像进行半色调处理。
|
||||
@@ -159,6 +160,7 @@ LayerMask: SegmentAnythingUltra, LayerMask: SegmentAnythingUltra V2, LayerMask:
|
||||
LayerUtility: UserPromptGeneratorTxt2ImgPrompt, LayerUtility: UserPromptGeneratorTxt2ImgPromptWithReference, LayerUtility: UserPromptGeneratorReplaceWord,
|
||||
LayerUtility: AddBlindWaterMark, LayerUtility: ShowBlindWaterMark, LayerMask: YoloV8Detect
|
||||
|
||||
* 添加 [LoadImageFromPath](LoadImageFromPath) 节点,可将一个文件夹中的图片加载为图片列表输出,支持单个文件名对应列表输出。
|
||||
* 合并[alexisrolland](https://github.com/alexisrolland) 提交的分支,添加Image Blend Advanced v3 和 Drop Shadow v3 节点,支持透明背景。
|
||||
* 添加[BenUltra](#BenUltra) 和 [LoadBenModel](#LoadBenModel)节点。这两个节点是[PramaLLC/BEN](https://huggingface.co/PramaLLC/BEN) 项目在ComfyUI中的实现。
|
||||
从 [huggingface](https://huggingface.co/PramaLLC/BEN/tree/main) 或 [百度网盘](https://pan.baidu.com/s/17mdBxfBl_R97mtNHuiHsxQ?pwd=2jn3)下载```BEN_Base.pth``` 和 ```config.json``` 两个文件并复制到 ```ComfyUI/models/BEN```文件夹。
|
||||
@@ -1555,6 +1557,28 @@ BooleanOperator的升级版,增加了节点内数值输入,增加了大于
|
||||
|
||||
<sup>*</sup>输入```%date```表示当前日期(YY-mm-dd),```%time```表示当前时间(HH-MM-SS)。可以输入```/```表示子目录。例如```%date/name_%time``` 将输出图片到```YY-mm-dd```文件夹下,以```name_HH-MM-SS```为文件名前缀。
|
||||
|
||||
### <a id="table1">ImageTaggerSaveV2</a>
|
||||
[ImageTaggerSave](#ImageTaggerSave) 节点的升级版,可配合[LoadImagesFromPath](#LoadImagesFromPath) 节点保存文件夹中对应图片的文本标签文件,并保持原文件名。
|
||||
|
||||
在原节点上增加了以下选项:
|
||||

|
||||
* custom_filename: 用户自定义文件名。如果此处有输入则作为保存文件名,否则使用filename_prefix作为文件名前缀。
|
||||
* remove_custom_filename_ext: 是否移除原文件名中的扩展名。
|
||||
|
||||
### <a id="table1">LoadImagesFromPath</a>
|
||||
从指定的文件夹中加载图片。
|
||||
|
||||
节点选项说明:
|
||||

|
||||
* path: 文件夹路径。
|
||||
* image_load_cap: 输出的文件数量。默认值0表示读取文件夹中的所有图片文件。
|
||||
* select_every_nth: 每隔多少张图片加载一张图片。
|
||||
|
||||
输出:
|
||||
* images: 输出的图片列表。
|
||||
* masks: 输出图片对应的遮罩列表。
|
||||
* file_name: 输出图片对应的文件名列表。
|
||||
* frame_count: 输出图片总数。
|
||||
|
||||
|
||||
# <a id="table1">LayerMask</a>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 168 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
+148
-2
@@ -1,4 +1,5 @@
|
||||
import os.path
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from PIL import Image
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
@@ -133,10 +134,155 @@ class LSImageTaggerSave:
|
||||
|
||||
return { "ui": { "images": results } }
|
||||
|
||||
class LSImageTaggerSave_V2:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.prefix_append = ""
|
||||
self.compress_level = 4
|
||||
self.NODE_NAME = 'ImageTaggerSaveV2'
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required":
|
||||
{"image": ("IMAGE", ),
|
||||
"tag_text": ("STRING", {"default": "", "forceInput":True}),
|
||||
"custom_path": ("STRING", {"default": ""}),
|
||||
"custom_filename": ("STRING", {"default": ""}),
|
||||
"remove_custom_filename_ext": ("BOOLEAN", {"default": True}),
|
||||
"filename_prefix": ("STRING", {"default": "comfyui"}),
|
||||
"timestamp": (["None", "second", "millisecond"],),
|
||||
"format": (["png", "jpg"],),
|
||||
"quality": ("INT", {"default": 80, "min": 10, "max": 100, "step": 1}),
|
||||
"preview": ("BOOLEAN", {"default": True}),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "image_tagger_save_v2"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = '😺dzNodes/LayerUtility/SystemIO'
|
||||
|
||||
def image_tagger_save_v2(self, image, tag_text, custom_path, custom_filename, remove_custom_filename_ext,
|
||||
filename_prefix, timestamp, format, quality,
|
||||
preview,
|
||||
prompt=None, extra_pnginfo=None):
|
||||
|
||||
now = datetime.datetime.now()
|
||||
custom_path = custom_path.replace("%date", now.strftime("%Y-%m-%d"))
|
||||
custom_path = custom_path.replace("%time", now.strftime("%H-%M-%S"))
|
||||
filename_prefix = filename_prefix.replace("%date", now.strftime("%Y-%m-%d"))
|
||||
filename_prefix = filename_prefix.replace("%time", now.strftime("%H-%M-%S"))
|
||||
filename_prefix += self.prefix_append
|
||||
|
||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir, image[0].shape[1], image[0].shape[0])
|
||||
|
||||
if custom_filename != "":
|
||||
if remove_custom_filename_ext:
|
||||
file = Path(custom_filename).stem
|
||||
else:
|
||||
file = custom_filename
|
||||
|
||||
if timestamp == "millisecond":
|
||||
file = f'{file}_{now.strftime("%Y-%m-%d_%H-%M-%S-%f")[:-3]}'
|
||||
elif timestamp == "second":
|
||||
file = f'{file}_{now.strftime("%Y-%m-%d_%H-%M-%S")}'
|
||||
|
||||
else:
|
||||
if timestamp == "millisecond":
|
||||
file = f'{filename}_{now.strftime("%Y-%m-%d_%H-%M-%S-%f")[:-3]}'
|
||||
elif timestamp == "second":
|
||||
file = f'{filename}_{now.strftime("%Y-%m-%d_%H-%M-%S")}'
|
||||
else:
|
||||
file = f'{filename}_{counter:08}'
|
||||
|
||||
results = list()
|
||||
temp_sub_dir = generate_random_name('_savepreview_', '_temp', 16)
|
||||
temp_dir = os.path.join(folder_paths.get_temp_directory(), temp_sub_dir)
|
||||
metadata = None
|
||||
i = 255. * image[0].cpu().numpy()
|
||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||
|
||||
preview_filename = ""
|
||||
if custom_path != "":
|
||||
if not os.path.exists(custom_path):
|
||||
try:
|
||||
os.makedirs(custom_path)
|
||||
except Exception as e:
|
||||
log(f"Error: {self.NODE_NAME} skipped, because unable to create temporary folder.",
|
||||
message_type='warning')
|
||||
raise FileNotFoundError(f"cannot create custom_path {custom_path}, {e}")
|
||||
else:
|
||||
custom_path = folder_paths.get_output_directory()
|
||||
|
||||
full_output_folder = os.path.normpath(custom_path)
|
||||
# save preview image to temp_dir
|
||||
if os.path.isdir(temp_dir):
|
||||
shutil.rmtree(temp_dir)
|
||||
try:
|
||||
os.makedirs(temp_dir)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
log(f"Error: {self.NODE_NAME} skipped, because unable to create temporary folder.",
|
||||
message_type='warning')
|
||||
try:
|
||||
preview_filename = os.path.join(generate_random_name('saveimage_preview_', '_temp', 16) + '.png')
|
||||
img.save(os.path.join(temp_dir, preview_filename))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
log(f"Error: {self.NODE_NAME} skipped, because unable to create temporary file.", message_type='warning')
|
||||
|
||||
if custom_filename == "":
|
||||
# check if file exists, change filename
|
||||
while os.path.isfile(os.path.join(full_output_folder, f"{file}.{format}")):
|
||||
counter += 1
|
||||
if timestamp == "millisecond":
|
||||
file = f'{filename}_{now.strftime("%Y-%m-%d_%H-%M-%S-%f")[:-3]}_{counter:08}'
|
||||
elif timestamp == "second":
|
||||
file = f'{filename}_{now.strftime("%Y-%m-%d_%H-%M-%S")}_{counter:08}'
|
||||
else:
|
||||
file = f"{filename}_{counter:08}"
|
||||
|
||||
|
||||
image_file_name = os.path.join(full_output_folder, f"{file}.{format}")
|
||||
tag_file_name = os.path.join(full_output_folder, f"{file}.txt")
|
||||
|
||||
if format == "png":
|
||||
img.save(image_file_name, pnginfo=metadata, compress_level= (100 - quality) // 10)
|
||||
else:
|
||||
if img.mode == "RGBA":
|
||||
img = img.convert("RGB")
|
||||
img.save(image_file_name, quality=quality)
|
||||
with open(tag_file_name, "w", encoding="utf-8") as f:
|
||||
f.write(remove_empty_lines(tag_text))
|
||||
log(f"{self.NODE_NAME} -> Saving image to {image_file_name}")
|
||||
|
||||
if preview:
|
||||
if custom_path == "":
|
||||
results.append({
|
||||
"filename": f"{file}.{format}",
|
||||
"subfolder": subfolder,
|
||||
"type": self.type
|
||||
})
|
||||
else:
|
||||
results.append({
|
||||
"filename": preview_filename,
|
||||
"subfolder": temp_sub_dir,
|
||||
"type": "temp"
|
||||
})
|
||||
|
||||
counter += 1
|
||||
|
||||
return { "ui": { "images": results } }
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: ImageTaggerSave": LSImageTaggerSave
|
||||
"LayerUtility: ImageTaggerSave": LSImageTaggerSave,
|
||||
"LayerUtility: ImageTaggerSaveV2": LSImageTaggerSave_V2
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: ImageTaggerSave": "LayerUtility: Image Tagger Save"
|
||||
"LayerUtility: ImageTaggerSave": "LayerUtility: Image Tagger Save",
|
||||
"LayerUtility: ImageTaggerSaveV2": "LayerUtility: Image Tagger Save V2",
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import os
|
||||
from PIL import Image, ImageSequence, ImageOps
|
||||
import torch
|
||||
import numpy as np
|
||||
import folder_paths
|
||||
import node_helpers
|
||||
|
||||
class LS_LoadImagesFromPath:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"path": ("STRING", {"placeholder": "c:/images", "images_path": []}),
|
||||
},
|
||||
"optional": {
|
||||
"image_load_cap": ("INT", {"default": 0, "min": 0, "max": 999999, "step": 1}),
|
||||
"select_every_nth": ("INT", {"default": 1, "min": 1, "max": 999999, "step": 1}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "MASK", "STRING", "INT")
|
||||
RETURN_NAMES = ("images", "masks", "file_name", "frame_count")
|
||||
FUNCTION = "ls_load_images"
|
||||
CATEGORY = '😺dzNodes/LayerUtility/SystemIO'
|
||||
OUTPUT_IS_LIST = (True, True, True, False)
|
||||
|
||||
|
||||
def ls_load_images(self, path: str, image_load_cap: int, select_every_nth: int):
|
||||
load_images = []
|
||||
load_masks = []
|
||||
load_file_names = []
|
||||
load_frame_count = 0
|
||||
|
||||
|
||||
if os.path.isdir(path):
|
||||
input_dir = os.path.normpath(path)
|
||||
files = [
|
||||
os.path.join(input_dir, f)
|
||||
for f in os.listdir(input_dir)
|
||||
if os.path.isfile(os.path.join(input_dir, f))
|
||||
]
|
||||
|
||||
for i in range(len(files)):
|
||||
|
||||
if i % select_every_nth != 0:
|
||||
continue
|
||||
|
||||
image_file = files[i]
|
||||
image_path = folder_paths.get_annotated_filepath(image_file)
|
||||
img = node_helpers.pillow(Image.open, image_path)
|
||||
output_images = []
|
||||
output_masks = []
|
||||
w, h = None, None
|
||||
|
||||
excluded_formats = ['MPO']
|
||||
|
||||
for i in ImageSequence.Iterator(img):
|
||||
i = node_helpers.pillow(ImageOps.exif_transpose, i)
|
||||
|
||||
if i.mode == 'I':
|
||||
i = i.point(lambda i: i * (1 / 255))
|
||||
image = i.convert("RGB")
|
||||
|
||||
if len(output_images) == 0:
|
||||
w = image.size[0]
|
||||
h = image.size[1]
|
||||
|
||||
if image.size[0] != w or image.size[1] != h:
|
||||
continue
|
||||
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = torch.from_numpy(image)[None,]
|
||||
if 'A' in i.getbands():
|
||||
mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0
|
||||
mask = 1. - torch.from_numpy(mask)
|
||||
elif i.mode == 'P' and 'transparency' in i.info:
|
||||
mask = np.array(i.convert('RGBA').getchannel('A')).astype(np.float32) / 255.0
|
||||
mask = 1. - torch.from_numpy(mask)
|
||||
else:
|
||||
mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu")
|
||||
output_images.append(image)
|
||||
output_masks.append(mask.unsqueeze(0))
|
||||
|
||||
if len(output_images) > 1 and img.format not in excluded_formats:
|
||||
output_image = torch.cat(output_images, dim=0)
|
||||
output_mask = torch.cat(output_masks, dim=0)
|
||||
else:
|
||||
output_image = output_images[0]
|
||||
output_mask = output_masks[0]
|
||||
|
||||
load_images.append(output_image)
|
||||
load_masks.append(output_mask)
|
||||
load_file_names.append(os.path.basename(image_file))
|
||||
load_frame_count += 1
|
||||
if image_load_cap > 0 and load_frame_count >= image_load_cap:
|
||||
break
|
||||
|
||||
return (load_images, load_masks, load_file_names, load_frame_count)
|
||||
|
||||
else:
|
||||
raise Exception("directory is not valid: " + directory)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: LoadImagesFromPath": LS_LoadImagesFromPath,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: LoadImagesFromPath": "LayerUtility: Load Images From Path",
|
||||
}
|
||||
+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 = "2.0.21"
|
||||
version = "2.0.22"
|
||||
license = {text = "MIT License"}
|
||||
dependencies = ["numpy", "pillow", "torch", "matplotlib", "Scipy", "scikit_image", "scikit_learn", "opencv-contrib-python", "pymatting", "timm", "colour-science", "transformers", "blend_modes", "huggingface_hub", "loguru"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user