Files
CavinHuang-comfyui-nodes-docs/docs/Image Overlay.md
2024-07-04 08:39:03 +08:00

7.1 KiB

Documentation

  • Class name: Image Overlay
  • Category: Efficiency Nodes/Image
  • Output node: False

Image Overlay节点用于将一个图像叠加到另一个图像上,并提供多种自定义选项,如调整大小、旋转、透明度调整和可选的遮罩。该节点通过对叠加图像应用变换和混合技术,然后将其与基础图像合并,从而实现创建复合图像。

Input types

Required

  • base_image
    • 作为叠加图像应用目标的基础图像。它充当复合图像的背景。
    • Comfy dtype: IMAGE
    • Python dtype: torch.Tensor
  • overlay_image
    • 要叠加到基础图像上的图像。在叠加之前,可以调整此图像的大小、旋转角度和透明度。
    • Comfy dtype: IMAGE
    • Python dtype: torch.Tensor
  • overlay_resize
    • 指定如何调整叠加图像的大小以适应基础图像,选项包括适应尺寸、按因子缩放或调整到特定的宽度和高度。
    • Comfy dtype: COMBO[STRING]
    • Python dtype: str
  • resize_method
    • 用于调整叠加图像大小的方法,影响调整后图像的质量和外观。
    • Comfy dtype: COMBO[STRING]
    • Python dtype: str
  • rescale_factor
    • 当选择"按rescale_factor调整大小"选项时,叠加图像的缩放因子。
    • Comfy dtype: FLOAT
    • Python dtype: float
  • width
    • 当选择"调整到指定宽度和高度"选项时,叠加图像调整后的宽度。
    • Comfy dtype: INT
    • Python dtype: int
  • height
    • 当选择"调整到指定宽度和高度"选项时,叠加图像调整后的高度。
    • Comfy dtype: INT
    • Python dtype: int
  • x_offset
    • 叠加图像在基础图像上放置的水平偏移量。
    • Comfy dtype: INT
    • Python dtype: int
  • y_offset
    • 叠加图像在基础图像上放置的垂直偏移量。
    • Comfy dtype: INT
    • Python dtype: int
  • rotation
    • 在应用到基础图像之前,叠加图像旋转的角度(以度为单位)。
    • Comfy dtype: INT
    • Python dtype: float
  • opacity
    • 叠加图像的不透明度,允许创建透明叠加效果。
    • Comfy dtype: FLOAT
    • Python dtype: float

Optional

  • optional_mask
    • 可选的遮罩,可应用于叠加图像,控制叠加图像不同部分的透明度。
    • Comfy dtype: MASK
    • Python dtype: Optional[torch.Tensor]

Output types

  • image
    • 应用叠加后的基础图像,反映了所有指定的变换和调整。
    • Comfy dtype: IMAGE
    • Python dtype: torch.Tensor

Usage tips

  • Infra type: GPU
  • Common nodes: unknown

Source code

class TSC_ImageOverlay:

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "base_image": ("IMAGE",),
                "overlay_image": ("IMAGE",),
                "overlay_resize": (["None", "Fit", "Resize by rescale_factor", "Resize to width & heigth"],),
                "resize_method": (["nearest-exact", "bilinear", "area"],),
                "rescale_factor": ("FLOAT", {"default": 1, "min": 0.01, "max": 16.0, "step": 0.1}),
                "width": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 64}),
                "height": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 64}),
                "x_offset": ("INT", {"default": 0, "min": -48000, "max": 48000, "step": 10}),
                "y_offset": ("INT", {"default": 0, "min": -48000, "max": 48000, "step": 10}),
                "rotation": ("INT", {"default": 0, "min": -180, "max": 180, "step": 5}),
                "opacity": ("FLOAT", {"default": 0, "min": 0, "max": 100, "step": 5}),
            },
            "optional": {"optional_mask": ("MASK",),}
        }

    RETURN_TYPES = ("IMAGE",)
    FUNCTION = "apply_overlay_image"
    CATEGORY = "Efficiency Nodes/Image"

    def apply_overlay_image(self, base_image, overlay_image, overlay_resize, resize_method, rescale_factor,
                            width, height, x_offset, y_offset, rotation, opacity, optional_mask=None):

        # Pack tuples and assign variables
        size = width, height
        location = x_offset, y_offset
        mask = optional_mask

        # Check for different sizing options
        if overlay_resize != "None":
            #Extract overlay_image size and store in Tuple "overlay_image_size" (WxH)
            overlay_image_size = overlay_image.size()
            overlay_image_size = (overlay_image_size[2], overlay_image_size[1])
            if overlay_resize == "Fit":
                h_ratio = base_image.size()[1] / overlay_image_size[1]
                w_ratio = base_image.size()[2] / overlay_image_size[0]
                ratio = min(h_ratio, w_ratio)
                overlay_image_size = tuple(round(dimension * ratio) for dimension in overlay_image_size)
            elif overlay_resize == "Resize by rescale_factor":
                overlay_image_size = tuple(int(dimension * rescale_factor) for dimension in overlay_image_size)
            elif overlay_resize == "Resize to width & heigth":
                overlay_image_size = (size[0], size[1])

            samples = overlay_image.movedim(-1, 1)
            overlay_image = comfy.utils.common_upscale(samples, overlay_image_size[0], overlay_image_size[1], resize_method, False)
            overlay_image = overlay_image.movedim(1, -1)
            
        overlay_image = tensor2pil(overlay_image)

         # Add Alpha channel to overlay
        overlay_image = overlay_image.convert('RGBA')
        overlay_image.putalpha(Image.new("L", overlay_image.size, 255))

        # If mask connected, check if the overlay_image image has an alpha channel
        if mask is not None:
            # Convert mask to pil and resize
            mask = tensor2pil(mask)
            mask = mask.resize(overlay_image.size)
            # Apply mask as overlay's alpha
            overlay_image.putalpha(ImageOps.invert(mask))

        # Rotate the overlay image
        overlay_image = overlay_image.rotate(rotation, expand=True)

        # Apply opacity on overlay image
        r, g, b, a = overlay_image.split()
        a = a.point(lambda x: max(0, int(x * (1 - opacity / 100))))
        overlay_image.putalpha(a)

        # Split the base_image tensor along the first dimension to get a list of tensors
        base_image_list = torch.unbind(base_image, dim=0)

        # Convert each tensor to a PIL image, apply the overlay, and then convert it back to a tensor
        processed_base_image_list = []
        for tensor in base_image_list:
            # Convert tensor to PIL Image
            image = tensor2pil(tensor)

            # Paste the overlay image onto the base image
            if mask is None:
                image.paste(overlay_image, location)
            else:
                image.paste(overlay_image, location, overlay_image)

            # Convert PIL Image back to tensor
            processed_tensor = pil2tensor(image)

            # Append to list
            processed_base_image_list.append(processed_tensor)

        # Combine the processed images back into a single tensor
        base_image = torch.stack([tensor.squeeze() for tensor in processed_base_image_list])

        # Return the edited base image
        return (base_image,)