ComfyUI-text-overlay icon indicating copy to clipboard operation
ComfyUI-text-overlay copied to clipboard

fixed the error and added 3 elements for anyone who wants it.

Open LockMan007 opened this issue 1 year ago • 2 comments

I know it is proper to do a "pull request" but I'm busy and this is for me and anyone else who wants to add some features, since this project has a bug from a year ago that was never fixed.

This adds the option to put the text in the bottom right, and adds an option for black text on an opaque white background.

It should be coded so that you can choose top, bottom, left, center, right, but most "watermarks" are added at the bottom right, and i had issues getting this to work because I had to "Fix Node (Recreate)" to finally fix the glitch that had me stumped.

It should also be coded to let you choose colors, but white background with black text I think is common enough and fine.

The opaque-size should also be calculated automatically, but isn't coded, so you have to manually adjust the number to ensure text doesn't go below the box. This just adds some height below the box, to accommodate for letters like "y, q, p" and so on.

Enjoy.

added-3-options

from PIL import Image, ImageDraw, ImageFont
import torch


import numpy as np

class ImageTextOverlay:
    def __init__(self, device="cpu"):
        self.device = device
    _alignments = ["left", "right", "center"]

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "image": ("IMAGE",),
                "text": ("STRING", {"multiline": True, "default": "Hello"}),
                "font_size": ("INT", {"default": 16, "min": 1, "max": 256, "step": 1}),
                "x": ("INT", {"default": 0}),
                "y": ("INT", {"default": 0}),
                "font": ("STRING", {"default": "arial.ttf"}),  # Assuming it's a path to a .ttf or .otf file
                "alignment": (cls._alignments, {"default": "left"}),  # ["left", "right", "center"]
                "color": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFF, "step": 1, "display": "color"}),
                "position_option": ("BOOLEAN", {"default": False, "label_on": "Using Bottom Right", "label_off": "Using X,Y Position", "tooltip": "Toggle to place text at the bottom right of the image or use custom X, Y positions."}),
                "background": ("BOOLEAN", {"default": False, "label_on": "Opaque", "label_off": "Transparent", "tooltip": "Toggle to add an opaque white background to the text with black font color."}),
                "opaque_size": ("INT", {"default": 10, "min": 0, "max": 50, "step": 1, "tooltip": "Adjust the size of the opaque background box."}),
            }
        }

    RETURN_TYPES = ("IMAGE",)
    FUNCTION = "draw_text_on_image"
    CATEGORY = "image/text"

    def draw_text_on_image(self, image, text, font_size, x, y, font, alignment, color, position_option, background, opaque_size):
        # Convert tensor to numpy array and then to PIL Image
        image_tensor = image
        image_np = image_tensor.cpu().numpy()  # Change from CxHxW to HxWxC for Pillow
        image = Image.fromarray((image_np.squeeze(0) * 255).astype(np.uint8))  # Convert float [0,1] tensor to uint8 image

        # Convert color from INT to RGB tuple
        r = (color >> 16) & 0xFF
        g = (color >> 8) & 0xFF
        b = color & 0xFF
        color_rgb = (r, g, b)

        # Load font
        loaded_font = ImageFont.truetype(font, font_size)

        # Prepare to draw on image
        draw = ImageDraw.Draw(image)

        # Adjust x and y coordinates based on ignore_position
        if position_option:
            text_width = draw.textlength(text, font=loaded_font)
            text_bbox = draw.textbbox((0, 0), text, font=loaded_font)
            text_height = text_bbox[3] - text_bbox[1] + opaque_size  # Add opaque_size to the height
            x = image.width - text_width - 10  # 10 pixels buffer from the right
            y = image.height - text_height - 10  # 10 pixels buffer from the bottom

        # Draw opaque background if needed
        if background:
            text_width = draw.textlength(text, font=loaded_font)
            text_bbox = draw.textbbox((0, 0), text, font=loaded_font)
            text_height = text_bbox[3] - text_bbox[1] + opaque_size  # Add opaque_size to the height
            draw.rectangle([x, y, x + text_width, y + text_height], fill="white")
            color_rgb = (0, 0, 0)  # Set text color to black

        # Adjust x coordinate based on alignment
        if alignment == "center":
            x -= text_width // 2
        elif alignment == "right":
            x -= text_width

        # Draw text on the image
        draw.text((x, y), text, fill=color_rgb, font=loaded_font)

        # Convert back to Tensor if needed
        image_tensor_out = torch.tensor(np.array(image).astype(np.float32) / 255.0)  # Convert back to CxHxW
        image_tensor_out = torch.unsqueeze(image_tensor_out, 0)

        return (image_tensor_out,)


NODE_CLASS_MAPPINGS = {
    "Image Text Overlay": ImageTextOverlay,
}

LockMan007 avatar Nov 28 '24 08:11 LockMan007

Thanks so much, this helped immensely! Any idea how to select "white" as a color? The input only texts a single integer. #FFFFFF or ffffff are converted NaN's, lol.

throvolos avatar Dec 24 '24 22:12 throvolos

White is 16777215, which is the hex number FFF in decimal. (perhaps there is a hex to decimal node that makes this easier, you can right click on the number input widget and convert it to an input)

mbylstra avatar Mar 17 '25 03:03 mbylstra