commit SaveImagePlusV2 node
This commit is contained in:
@@ -145,6 +145,7 @@ Please try downgrading the ```protobuf``` dependency package to 3.20.3, or set e
|
||||
|
||||
**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.
|
||||
|
||||
* Commit [SaveImagePLusV2](SaveImagePlusV2) node, add custom file names and setting up the dpi of image.
|
||||
* Commit [GeminiImageEdit](#GeminiImageEdit) node, support using gemini-2.0-flash-exp-image-generation API for image editing.
|
||||
* Commit [GeminiV2](#GeminiV2) and [ObjectDetectorGeminiV2](#ObjectDetectorGeminiV2) nodes, used google-genai dependency package that supports the gemini-2.0-flash-exp and gemini-2.5-pro-exp-03-25 models.
|
||||
* Add QuarkNetdisk model download link.
|
||||
@@ -716,7 +717,7 @@ Enhanced save image node. You can customize the directory where the picture is s
|
||||
Node Options:
|
||||

|
||||
|
||||
* iamge: The input image.
|
||||
* image: The input image.
|
||||
* custom_path<sup>*</sup>: User-defined directory, enter the directory name in the correct format. If empty, it is saved in the default output directory of ComfyUI.
|
||||
* filename_prefix<sup>*</sup>: The prefix of file name.
|
||||
* timestamp: Timestamp the file name, opting for date, time to seconds, and time to milliseconds.
|
||||
@@ -729,6 +730,17 @@ 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">SaveImagePlusV2</a>
|
||||
Added custom file name and dpi options on the SaveImagePlus node.
|
||||
|
||||
NodeOptions:
|
||||

|
||||
|
||||
* custom_filename<sup>*</sup>: User defined file name, if entered here, will be used as the file name. Please note that duplicate files will be overwritten. If this is empty, use the file name prefix and timestamp as the file name.
|
||||
* dpi: Set the DPI value of the image file.
|
||||
|
||||
<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">AddBlindWaterMark</a>
|
||||
|
||||
+14
-1
@@ -121,6 +121,7 @@ If this call came from a _pb2.py file, your generated code is out of date and mu
|
||||
## 更新说明
|
||||
**如果本插件更新后出现依赖包错误,请双击运行插件目录下的```install_requirements.bat```(官方便携包),或 ```install_requirements_aki.bat```(秋叶整合包) 重新安装依赖包。
|
||||
|
||||
* 添加 [SaveImagePLusV2](SaveImagePlusV2) 节点,增加自定义文件名和设置dpi。
|
||||
* 添加多语言,除英语外,增加支持5种语言:中文、法语、日语、韩语和俄语。该功能使用[ComfyUI-Globalization-Node-Translation](https://github.com/yamanacn/ComfyUI-Globalization-Node-Translation)制作,感谢原作者。
|
||||
* 添加 [GeminiImageEdit](#GeminiImageEdit) 节点,支持使用gemini-2.0-flash-exp-image-generation API进行图像编辑。
|
||||
* 添加 [GeminiV2](#GeminiV2) 以及 [ObjectDetectorGeminiV2](#ObjectDetectorGeminiV2) 节点,使用了新的google-genai依赖包,支持最新的gemini-2.0-flash-exp以及gemini-2.5-pro-exp-03-25模型。
|
||||
@@ -667,7 +668,7 @@ box_preview: 裁切位置预览。
|
||||
|
||||
节点选项说明:
|
||||

|
||||
* iamge: 输入的图片。
|
||||
* image: 输入的图片。
|
||||
* custom_path<sup>*</sup>: 用户自定义目录,请按正确的格式输入目录名。如果为空则保存在ComfyUI默认的output目录。
|
||||
* filename_prefix<sup>*</sup>:文件名前缀。。
|
||||
* timestamp: 为文件名加上时间戳,可选择日期、时间到秒和时间到毫秒。
|
||||
@@ -680,6 +681,18 @@ box_preview: 裁切位置预览。
|
||||
|
||||
<sup>*</sup>输入```%date```表示当前日期(YY-mm-dd),```%time```表示当前时间(HH-MM-SS)。可以输入```/```表示子目录。例如```%date/name_%time``` 将输出图片到```YY-mm-dd```文件夹下,以```name_HH-MM-SS```为文件名前缀。
|
||||
|
||||
### <a id="table1">SaveImagePlusV2</a>
|
||||
在SaveImagePlus节点上增加了自定义文件名和dpi选项。
|
||||
|
||||
节点选项说明:
|
||||

|
||||
|
||||
* custom_filename<sup>*</sup>: 用户自定义文件名,如果此处有输入,将以此为文件名,注意重名文件将被覆盖。此处为空则按照文件名前缀+时间戳作为文件名。
|
||||
* dpi: 设置图片文件的dpi值。
|
||||
|
||||
<sup>*</sup>输入```%date```表示当前日期(YY-mm-dd),```%time```表示当前时间(HH-MM-SS)。可以输入```/```表示子目录。例如```%date/name_%time``` 将输出图片到```YY-mm-dd```文件夹下,以```name_HH-MM-SS```为文件名前缀。
|
||||
|
||||
|
||||
|
||||
### <a id="table1">AddBlindWaterMark</a>
|
||||

|
||||
|
||||
+187
-7
@@ -6,10 +6,10 @@ from PIL.PngImagePlugin import PngInfo
|
||||
import datetime
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'SaveImagePlus'
|
||||
|
||||
class SaveImagePlus:
|
||||
def __init__(self):
|
||||
self.NODE_NAME = 'SaveImagePlus'
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.prefix_append = ""
|
||||
@@ -102,7 +102,7 @@ class SaveImagePlus:
|
||||
try:
|
||||
os.makedirs(custom_path)
|
||||
except Exception as e:
|
||||
log(f"Error: {NODE_NAME} skipped, because unable to create temporary folder.",
|
||||
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}")
|
||||
|
||||
@@ -114,14 +114,14 @@ class SaveImagePlus:
|
||||
os.makedirs(temp_dir)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
log(f"Error: {NODE_NAME} skipped, because unable to create temporary folder.",
|
||||
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: {NODE_NAME} skipped, because unable to create temporary file.", message_type='warning')
|
||||
log(f"Error: {self.NODE_NAME} skipped, because unable to create temporary file.", message_type='warning')
|
||||
|
||||
# check if file exists, change filename
|
||||
while os.path.isfile(os.path.join(full_output_folder, f"{file}.{format}")):
|
||||
@@ -142,7 +142,7 @@ class SaveImagePlus:
|
||||
if img.mode == "RGBA":
|
||||
img = img.convert("RGB")
|
||||
img.save(image_file_name, quality=quality)
|
||||
log(f"{NODE_NAME} -> Saving image to {image_file_name}")
|
||||
log(f"{self.NODE_NAME} -> Saving image to {image_file_name}")
|
||||
|
||||
if save_workflow_as_json:
|
||||
try:
|
||||
@@ -174,10 +174,190 @@ class SaveImagePlus:
|
||||
|
||||
return { "ui": { "images": results } }
|
||||
|
||||
class SaveImagePlusV2:
|
||||
def __init__(self):
|
||||
self.NODE_NAME = 'SaveImagePlusV2'
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.prefix_append = ""
|
||||
self.compress_level = 4
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required":
|
||||
{"images": ("IMAGE", ),
|
||||
"custom_path": ("STRING", {"default": ""}),
|
||||
"custom_filename": ("STRING", {"default": ""}),
|
||||
"filename_prefix": ("STRING", {"default": "comfyui"}),
|
||||
"timestamp": (["None", "second", "millisecond"],),
|
||||
"dpi": ("INT", {"default": 96, "min": 1, "max": 10000, "step": 1}),
|
||||
"format": (["png", "jpg"],),
|
||||
"quality": ("INT", {"default": 80, "min": 10, "max": 100, "step": 1}),
|
||||
"meta_data": ("BOOLEAN", {"default": False}),
|
||||
"blind_watermark": ("STRING", {"default": ""}),
|
||||
"save_workflow_as_json": ("BOOLEAN", {"default": False}),
|
||||
"preview": ("BOOLEAN", {"default": True}),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "save_image_plus_v2"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = '😺dzNodes/LayerUtility/SystemIO'
|
||||
|
||||
def save_image_plus_v2(self, images, custom_path, custom_filename, filename_prefix,
|
||||
timestamp, dpi, format, quality,
|
||||
meta_data, blind_watermark, preview, save_workflow_as_json,
|
||||
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, images[0].shape[1], images[0].shape[0])
|
||||
|
||||
results = list()
|
||||
temp_sub_dir = generate_random_name('_savepreview_', '_temp', 16)
|
||||
temp_dir = os.path.join(folder_paths.get_temp_directory(), temp_sub_dir)
|
||||
for image in images:
|
||||
i = 255. * image.cpu().numpy()
|
||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||
|
||||
if blind_watermark != "":
|
||||
img_mode = img.mode
|
||||
wm_size = watermark_image_size(img)
|
||||
import qrcode
|
||||
qr = qrcode.QRCode(
|
||||
version=1,
|
||||
error_correction=qrcode.constants.ERROR_CORRECT_H,
|
||||
box_size=20,
|
||||
border=1,
|
||||
)
|
||||
qr.add_data(blind_watermark.encode('utf-8'))
|
||||
qr.make(fit=True)
|
||||
qr_image = qr.make_image(fill_color="black", back_color="white")
|
||||
qr_image = qr_image.resize((wm_size, wm_size), Image.BICUBIC).convert("L")
|
||||
|
||||
y, u, v, _ = image_channel_split(img, mode='YCbCr')
|
||||
_u = add_invisibal_watermark(u, qr_image)
|
||||
wm_img = image_channel_merge((y, _u, v), mode='YCbCr')
|
||||
|
||||
if img.mode == "RGBA":
|
||||
img = RGB2RGBA(wm_img, img.split()[-1])
|
||||
else:
|
||||
img = wm_img.convert(img_mode)
|
||||
|
||||
metadata = None
|
||||
if meta_data:
|
||||
metadata = PngInfo()
|
||||
if prompt is not None:
|
||||
metadata.add_text("prompt", json.dumps(prompt))
|
||||
if extra_pnginfo is not None:
|
||||
for x in extra_pnginfo:
|
||||
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
||||
|
||||
if custom_filename == "":
|
||||
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:05}'
|
||||
else:
|
||||
custom_filename = custom_filename.replace("%date", now.strftime("%Y-%m-%d"))
|
||||
custom_filename = custom_filename.replace("%time", now.strftime("%H-%M-%S"))
|
||||
filename = custom_filename
|
||||
file = filename
|
||||
|
||||
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}")
|
||||
|
||||
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), pnginfo=metadata, compress_level=(100 - quality) // 10, dpi=(dpi, dpi))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
log(f"Error: {self.NODE_NAME} skipped, because unable to create temporary file.", message_type='warning')
|
||||
|
||||
# check if file exists, change filename
|
||||
if custom_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:05}'
|
||||
elif timestamp == "second":
|
||||
file = f'{filename}_{now.strftime("%Y-%m-%d_%H-%M-%S")}_{counter:05}'
|
||||
else:
|
||||
file = f"{filename}_{counter:05}"
|
||||
|
||||
image_file_name = os.path.join(full_output_folder, f"{file}.{format}")
|
||||
json_file_name = os.path.join(full_output_folder, f"{file}.json")
|
||||
|
||||
if format == "png":
|
||||
img.save(image_file_name, pnginfo=metadata, compress_level=(100 - quality) // 10, dpi=(dpi, dpi))
|
||||
else:
|
||||
if img.mode == "RGBA":
|
||||
img = img.convert("RGB")
|
||||
img.save(image_file_name, quality=quality, dpi=(dpi, dpi))
|
||||
log(f"{self.NODE_NAME} -> Saving image to {image_file_name}")
|
||||
|
||||
if save_workflow_as_json:
|
||||
try:
|
||||
workflow = (extra_pnginfo or {}).get('workflow')
|
||||
if workflow is None:
|
||||
log('No workflow found, skipping saving of JSON')
|
||||
with open(f'{json_file_name}', 'w', encoding='utf-8') as workflow_file:
|
||||
json.dump(workflow, workflow_file, ensure_ascii=False, indent=4)
|
||||
log(f'Saved workflow to {json_file_name}')
|
||||
except Exception as e:
|
||||
log(
|
||||
f'Failed to save workflow as json due to: {e}, proceeding with the remainder of saving execution', message_type="warning")
|
||||
|
||||
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: SaveImagePlus": SaveImagePlus
|
||||
"LayerUtility: SaveImagePlus": SaveImagePlus,
|
||||
"LayerUtility: SaveImagePlusV2": SaveImagePlusV2
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: SaveImagePlus": "LayerUtility: SaveImage Plus(Advance)"
|
||||
"LayerUtility: SaveImagePlus": "LayerUtility: SaveImage Plus(Advance)",
|
||||
"LayerUtility: SaveImagePlusV2": "LayerUtility: SaveImagePlus V2(Advance)"
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui_layerstyle_advance"
|
||||
description = "The nodes detached from ComfyUI Layer Style are mainly those with complex requirements for dependency packages."
|
||||
version = "2.0.17"
|
||||
version = "2.0.18"
|
||||
license = { text = "MIT License" }
|
||||
dependencies = ["numpy", "matplotlib", "scikit_image", "scikit_learn", "opencv-contrib-python", "pymatting", "timm", "blend_modes", "transformers", "diffusers", "loguru", "colour-science", "huggingface_hub", "segment_anything", "addict", "omegaconf", "yapf", "wget", "iopath", "mediapipe", "typer_config", "fastapi", "rich", "google-generativeai", "ultralytics", "transparent-background", "accelerate", "onnxruntime", "bitsandbytes", "peft", "protobuf", "hydra-core", "blind-watermark", "qrcode", "pyzbar", "psd-tools", "wandb", "zhipuai", "openai","google-genai"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user