From 5b0e970b6f3db87a90fc24abc578e602ef81abe5 Mon Sep 17 00:00:00 2001 From: "kyj@bowong.ai" Date: Mon, 28 Jul 2025 18:14:25 +0800 Subject: [PATCH] =?UTF-8?q?ADD=20=E5=A2=9E=E5=8A=A0=E4=BB=BB=E6=84=8F?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=9B=BE=E7=89=87=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __init__.py | 8 ++++-- nodes/image_nodes.py | 67 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/__init__.py b/__init__.py index dd65599..68e2848 100644 --- a/__init__.py +++ b/__init__.py @@ -2,7 +2,7 @@ from .nodes.image_modal_nodes import ModalEditCustom, ModalClothesMask, ModalMid ModalMidJourneyDescribeImage from .nodes.image_face_nodes import FaceDetect, FaceExtract from .nodes.image_gesture_nodes import JMGestureCorrect, JMCustom -from .nodes.image_nodes import SaveImagePath, SaveImageWithOutput, LoadImgOptional +from .nodes.image_nodes import SaveImagePath, SaveImageWithOutput, LoadImgOptional, SaveImageAnywhere from .nodes.llm_nodes import LLMChat, LLMChatMultiModalImageUpload, LLMChatMultiModalImageTensor, Jinja2RenderTemplate from .nodes.object_storage_nodes import COSUpload, COSDownload, S3Download, S3Upload, S3UploadURL, S3UploadIMAGEURL from .nodes.text_nodes import StringEmptyJudgement, LoadText, RandomLineSelector @@ -49,7 +49,8 @@ NODE_CLASS_MAPPINGS = { "ModalMidJourneyGenerateImage": ModalMidJourneyGenerateImage, "ModalMidJourneyDescribeImage": ModalMidJourneyDescribeImage, "JMCustom": JMCustom, - "VideoMerge": VideoMerge + "VideoMerge": VideoMerge, + "SaveImageAnywhere": SaveImageAnywhere } NODE_DISPLAY_NAME_MAPPINGS = { @@ -89,5 +90,6 @@ NODE_DISPLAY_NAME_MAPPINGS = { "ModalMidJourneyGenerateImage": "Prompt生/修图-MJ", "ModalMidJourneyDescribeImage": "反推生图提示词-MJ", "JMCustom": "Prompt生视频", - "VideoMerge":"顺序合并视频" + "VideoMerge":"顺序合并视频", + "SaveImageAnywhere": "保存图片-任意路径" } diff --git a/nodes/image_nodes.py b/nodes/image_nodes.py index 68aa7c7..57f9f4d 100644 --- a/nodes/image_nodes.py +++ b/nodes/image_nodes.py @@ -191,3 +191,70 @@ class SaveImageWithOutput: counter = 1 return full_output_folder, filename_prefix, counter, subfolder, filename_prefix + + +class SaveImageAnywhere: + """保存图片并输出的节点:既保存图片又能继续传递数据""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "filename_prefix": ("STRING", {"default": "ComfyUI"}), + "output_dir": ("STRING", {"default": "output"}), + }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("file_path",) + FUNCTION = "save_image" + OUTPUT_NODE = True + CATEGORY = "不忘科技-自定义节点🚩/图片" + + def save_image(self, images, filename_prefix="ComfyUI", output_dir="output"): + full_output_folder, filename, counter, subfolder, filename_prefix = self.get_save_image_path( + filename_prefix,output_dir) + + file_paths = [] + + for i, image in enumerate(images): + # 转换图片数据 + img_array = 255. * image.cpu().numpy() + img = Image.fromarray(np.clip(img_array, 0, 255).astype(np.uint8)) + + # 生成文件名 + file = f"{filename_prefix}_{counter + i:05}_.png" + file_path = os.path.join(full_output_folder, file) + + # 保存图片 + img.save(file_path, compress_level=4) + file_paths.append(file_path) + + print(f"✅ 图片已保存到: {file_paths if file_paths else '未知路径'}") + + return (file_paths[0] if file_paths else "",) + + def get_save_image_path(self, filename_prefix, output_dir): + def map_filename(filename): + prefix_len = len(os.path.basename(filename_prefix)) + prefix = filename[:prefix_len + 1] + try: + digits = int(filename[prefix_len + 1:].split('_')[0]) + except: + digits = 0 + return (digits, prefix) + + subfolder = "" + full_output_folder = os.path.join(output_dir, subfolder) + + try: + counter = max(filter(lambda a: a[1][:-1] == filename_prefix and a[1][-1] == "_", + map(map_filename, os.listdir(full_output_folder))))[0] + 1 + except ValueError: + counter = 1 + except FileNotFoundError: + os.makedirs(full_output_folder, exist_ok=True) + counter = 1 + + return full_output_folder, filename_prefix, counter, subfolder, filename_prefix \ No newline at end of file