Files
CavinHuang-comfyui-nodes-docs/docs/CR Image Output.md
2024-05-26 20:35:16 +08:00

6.9 KiB

Documentation

CR_ImageOutput 是一个用于管理图像数据输出的节点,提供保存或预览图像的功能,并允许自定义文件命名和格式。它在图像处理工作流的最后阶段扮演着关键角色,确保结果易于访问且组织良好。

Input types

Required

  • images
    • 'image' 参数至关重要,因为它定义了节点将处理的输入图像数据。它通过决定将保存或预览的内容来影响节点的执行。
    • Comfy dtype: IMAGE
    • Python dtype: torch.Tensor
  • output_type
    • “output_type”参数决定节点是将图像保存到磁盘还是预览它们。它很重要,因为它将节点的主要功能引向任一输出方法。
    • Comfy dtype: COMBO['Preview', 'Save', 'UI (no batch)']
    • Python dtype: str
  • file_format
    • “file_format”参数指定了图像将被保存的格式,影响输出文件的质量和兼容性。
    • Comfy dtype: COMBO['png', 'jpg', 'webp', 'tif']
    • Python dtype: str

Optional

  • filename_prefix
    • “filename_prefix”参数设置输出文件的基本名称,这对于组织和识别保存的图像很重要。
    • Comfy dtype: STRING
    • Python dtype: str
  • prefix_presets
    • “prefix_presets”允许在文件名前添加基于日期的前缀,这对于按时间顺序排序和归档很有用。
    • Comfy dtype: COMBO[None, 'yyyyMMdd']
    • Python dtype: str
  • trigger
    • 当设置为 True 时,“trigger”参数在工作流中启动一个动作或信号,可能触发后续流程。
    • Comfy dtype: BOOLEAN
    • Python dtype: bool
  • prompt
    • “prompt”参数用于存储与图像相关的描述或注释,这对于元数据目的很有帮助。
    • Comfy dtype: PROMPT
    • Python dtype: str
  • extra_pnginfo
    • “extra_pnginfo”参数允许在PNG文件中嵌入额外的元数据,增强与图像相关的信息。
    • Comfy dtype: EXTRA_PNGINFO
    • Python dtype: Dict[str, Any]

Output types

  • ui
    • “ui”输出包含带有元数据的图像对象列表,这对于为用户界面提供已处理图像的必要视觉和上下文信息很重要。
    • Comfy dtype: COMBO[{'images': List[Dict[str, Any]]}]
    • Python dtype: Dict[str, Any]
  • result
    • “result”输出表示节点操作的完成状态,并在需要时提供用于进一步帮助的 URL。
    • Comfy dtype: COMBO[Tuple[BOOLEAN, str]]
    • Python dtype: Tuple[bool, str]

Usage tips

  • Infra type: CPU

Source code

class CR_ImageOutput:

    def __init__(self):
        self.output_dir = folder_paths.get_output_directory()
        self.type = 'output'

    @classmethod
    def INPUT_TYPES(cls):
        presets = ['None', 'yyyyMMdd']
        return {'required': {'images': ('IMAGE',), 'output_type': (['Preview', 'Save', 'UI (no batch)'],), 'filename_prefix': ('STRING', {'default': 'CR'}), 'prefix_presets': (presets,), 'file_format': (['png', 'jpg', 'webp', 'tif'],)}, 'hidden': {'prompt': 'PROMPT', 'extra_pnginfo': 'EXTRA_PNGINFO'}, 'optional': {'trigger': ('BOOLEAN', {'default': False})}}
    RETURN_TYPES = ('BOOLEAN',)
    RETURN_NAMES = ('trigger',)
    FUNCTION = 'save_images'
    OUTPUT_NODE = True
    CATEGORY = icons.get('Comfyroll/Essential/Core')

    def save_images(self, images, file_format, prefix_presets, filename_prefix='CR', trigger=False, output_type='Preview', prompt=None, extra_pnginfo=None):
        show_help = 'https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Core-Nodes#cr-image-output'

        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)
        if output_type == 'Save':
            self.output_dir = folder_paths.get_output_directory()
            self.type = 'output'
        elif output_type == 'Preview':
            self.output_dir = folder_paths.get_temp_directory()
            self.type = 'temp'
        date_formats = {'yyyyMMdd': lambda d: '{}{:02d}{:02d}'.format(str(d.year), d.month, d.day)}
        current_datetime = datetime.datetime.now()
        for (format_key, format_lambda) in date_formats.items():
            preset_prefix = f'{format_lambda(current_datetime)}'
        if prefix_presets != 'None':
            filename_prefix = filename_prefix + '_' + preset_prefix
        if filename_prefix[0] == '_':
            filename_prefix = filename_prefix[1:]
        subfolder = os.path.dirname(os.path.normpath(filename_prefix))
        filename = os.path.basename(os.path.normpath(filename_prefix))
        full_output_folder = os.path.join(self.output_dir, subfolder)
        if os.path.commonpath((self.output_dir, os.path.abspath(full_output_folder))) != self.output_dir:
            return {}
        try:
            counter = max(filter(lambda a: a[1][:-1] == filename 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
        if output_type == 'UI (no batch)':
            results = []
            for tensor in images:
                array = 255.0 * tensor.cpu().numpy()
                image = Image.fromarray(np.clip(array, 0, 255).astype(np.uint8))
                server = PromptServer.instance
                server.send_sync(BinaryEventTypes.UNENCODED_PREVIEW_IMAGE, ['PNG', image, None], server.client_id)
                results.append({'source': 'websocket', 'content-type': 'image/png', 'type': 'output'})
            return {'ui': {'images': results}}
        else:
            results = list()
            for image in images:
                i = 255.0 * image.cpu().numpy()
                img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
                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]))
                file_name = f'{filename}_{counter:05}_.{file_format}'
                img_params = {'png': {'compress_level': 4}, 'webp': {'method': 6, 'lossless': False, 'quality': 80}, 'jpg': {'format': 'JPEG'}, 'tif': {'format': 'TIFF'}}
                resolved_image_path = os.path.join(full_output_folder, file_name)
                img.save(resolved_image_path, **img_params[file_format], pnginfo=metadata)
                results.append({'filename': file_name, 'subfolder': subfolder, 'type': self.type})
                counter += 1
            return {'ui': {'images': results}, 'result': (trigger, show_help)}