Skip to content

🪛 Save image with extra metadata

Documentation

  • Class name: Save image with extra metadata [Crystools]
  • Category: crystools 🪛/Image
  • Output node: True

This node is designed to save images with additional metadata, which can include workflow details and custom metadata provided by the user. It extends the functionality of a standard image saving process by allowing the inclusion of extra information directly within the image file, enhancing traceability and context for each saved image.

Input types

Required

  • image
    • The image to be saved. This parameter is crucial as it determines the content that will be stored and subsequently used for further processing or reference.
    • Comfy dtype: IMAGE
    • Python dtype: PIL.Image.Image
  • filename_prefix
    • A prefix for the filename under which the image will be saved. This helps in organizing and identifying images easily, especially when dealing with multiple images or batches.
    • Comfy dtype: STRING
    • Python dtype: str
  • with_workflow
    • A flag indicating whether workflow details should be included in the image's metadata. This can be useful for tracking the processing history or steps involved in generating the image.
    • Comfy dtype: BOOLEAN
    • Python dtype: bool

Optional

  • metadata_extra
    • Additional metadata in JSON format to be included with the image. This provides flexibility in embedding various types of information as part of the image's metadata.
    • Comfy dtype: STRING
    • Python dtype: str

Output types

  • Metadata RAW
    • Comfy dtype: METADATA_RAW
    • The result of the image saving process, including the saved image's path and any associated metadata.
    • Python dtype: dict

Usage tips

  • Infra type: CPU
  • Common nodes: unknown

Source code

class CImageSaveWithExtraMetadata(SaveImage):
    def __init__(self):
        super().__init__()
        self.data_cached = None
        self.data_cached_text = None

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                # if it is required, in next node does not receive any value even the cache!
                "image": ("IMAGE",),
                "filename_prefix": ("STRING", {"default": "ComfyUI"}),
                "with_workflow": BOOLEAN,
            },
            "optional": {
                "metadata_extra": ("STRING", {"multiline": True, "default": json.dumps({
                  "Title": "Image generated by Crystian",
                  "Description": "More info: https:\/\/www.instagram.com\/crystian.ia", # "\/" is for escape / on json
                  "Author": "crystian.ia",
                  "Software": "ComfyUI",
                  "Category": "StableDiffusion",
                  "Rating": 5,
                  "UserComment": "",
                  "Keywords": [
                    ""
                  ],
                  "Copyrights": "",
                }, indent=CONFIG["indent"]).replace("\\/", "/"),
                }),
            },
            "hidden": {
                "prompt": "PROMPT",
                "extra_pnginfo": "EXTRA_PNGINFO",
            },
        }

    CATEGORY = CATEGORY.MAIN.value + CATEGORY.IMAGE.value
    RETURN_TYPES = ("METADATA_RAW",)
    RETURN_NAMES = ("Metadata RAW",)
    OUTPUT_NODE = True

    FUNCTION = "execute"

    def execute(self, image=None, filename_prefix="ComfyUI", with_workflow=True, metadata_extra=None, prompt=None, extra_pnginfo=None):
        data = {
            "result": [''],
            "ui": {
                "text": [''],
                "images": [],
            }
        }
        if image is not None:
            if with_workflow is True:
                extra_pnginfo_new = extra_pnginfo.copy()
                prompt = prompt.copy()
            else:
                extra_pnginfo_new = None
                prompt = None

            if metadata_extra is not None and metadata_extra != 'undefined':
                try:
                    metadata_extra = json.loads(f"{{{metadata_extra}}}")
                except Exception as e:
                    logger.error(f"Error parsing metadata_extra (it will send as string), error: {e}")
                    metadata_extra = {"extra": str(metadata_extra)}

                if isinstance(metadata_extra, dict):
                    for k, v in metadata_extra.items():
                        if extra_pnginfo_new is None:
                            extra_pnginfo_new = {}

                        extra_pnginfo_new[k] = v

            saved = super().save_images(image, filename_prefix, prompt, extra_pnginfo_new)

            image = saved["ui"]["images"][0]
            image_path = Path(self.output_dir).joinpath(image["subfolder"], image["filename"])
            img, promptFromImage, metadata = buildMetadata(image_path)

            images = [image]
            result = metadata

            data["result"] = [result]
            data["ui"]["images"] = images

        else:
            logger.debug("Source: Empty on CImageSaveWithExtraMetadata")

        return data