Skip to content

SaveAnimatedWEBP

Documentation

  • Class name: SaveAnimatedWEBP
  • Category: image/animation
  • Output node: True

This node is designed for saving a sequence of images as an animated WEBP file. It handles the aggregation of individual frames into a cohesive animation, applying specified metadata, and optimizing the output based on quality and compression settings.

Input types

Required

  • images
    • A list of images to be saved as frames in the animated WEBP. This parameter is essential for defining the visual content of the animation.
    • Comfy dtype: IMAGE
    • Python dtype: List[PIL.Image]
  • filename_prefix
    • Specifies the base name for the output file, which will be appended with a counter and the '.webp' extension. This parameter is crucial for identifying and organizing the saved files.
    • Comfy dtype: STRING
    • Python dtype: str
  • fps
    • The frames per second rate for the animation, influencing the playback speed.
    • Comfy dtype: FLOAT
    • Python dtype: float
  • lossless
    • A boolean indicating whether to use lossless compression, affecting the file size and quality of the animation.
    • Comfy dtype: BOOLEAN
    • Python dtype: bool
  • quality
    • A value between 0 and 100 that sets the compression quality level, with higher values resulting in better image quality but larger file sizes.
    • Comfy dtype: INT
    • Python dtype: int
  • method
    • Specifies the compression method to use, which can impact the encoding speed and file size.
    • Comfy dtype: COMBO[STRING]
    • Python dtype: str

Output types

  • ui
    • Provides a UI component displaying the saved animated WEBP images along with their metadata, and indicates whether the animation is enabled.

Usage tips

  • Infra type: CPU
  • Common nodes: unknown

Source code

class SaveAnimatedWEBP:
    def __init__(self):
        self.output_dir = folder_paths.get_output_directory()
        self.type = "output"
        self.prefix_append = ""

    methods = {"default": 4, "fastest": 0, "slowest": 6}
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"images": ("IMAGE", ),
                     "filename_prefix": ("STRING", {"default": "ComfyUI"}),
                     "fps": ("FLOAT", {"default": 6.0, "min": 0.01, "max": 1000.0, "step": 0.01}),
                     "lossless": ("BOOLEAN", {"default": True}),
                     "quality": ("INT", {"default": 80, "min": 0, "max": 100}),
                     "method": (list(s.methods.keys()),),
                     # "num_frames": ("INT", {"default": 0, "min": 0, "max": 8192}),
                     },
                "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
                }

    RETURN_TYPES = ()
    FUNCTION = "save_images"

    OUTPUT_NODE = True

    CATEGORY = "image/animation"

    def save_images(self, images, fps, filename_prefix, lossless, quality, method, num_frames=0, prompt=None, extra_pnginfo=None):
        method = self.methods.get(method)
        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()
        pil_images = []
        for image in images:
            i = 255. * image.cpu().numpy()
            img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
            pil_images.append(img)

        metadata = pil_images[0].getexif()
        if not args.disable_metadata:
            if prompt is not None:
                metadata[0x0110] = "prompt:{}".format(json.dumps(prompt))
            if extra_pnginfo is not None:
                inital_exif = 0x010f
                for x in extra_pnginfo:
                    metadata[inital_exif] = "{}:{}".format(x, json.dumps(extra_pnginfo[x]))
                    inital_exif -= 1

        if num_frames == 0:
            num_frames = len(pil_images)

        c = len(pil_images)
        for i in range(0, c, num_frames):
            file = f"{filename}_{counter:05}_.webp"
            pil_images[i].save(os.path.join(full_output_folder, file), save_all=True, duration=int(1000.0/fps), append_images=pil_images[i + 1:i + num_frames], exif=metadata, lossless=lossless, quality=quality, method=method)
            results.append({
                "filename": file,
                "subfolder": subfolder,
                "type": self.type
            })
            counter += 1

        animated = num_frames != 1
        return { "ui": { "images": results, "animated": (animated,) } }