| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | import torch
|
| | import numpy as np
|
| | from PIL import Image, ImageDraw, ImageFont
|
| |
|
| |
|
| |
|
| |
|
| | def add_watermark(image, font_path,
|
| | watermark_text_line1="立香", font_size_line1=40,
|
| | watermark_text_line2="美羽", font_size_line2=30):
|
| | """
|
| | Adds a two-line watermark to an image with individually adjustable font sizes.
|
| | """
|
| | if not isinstance(image, Image.Image):
|
| | raise ValueError("Input must be a PIL Image object")
|
| |
|
| | if image.mode != 'RGBA':
|
| | image = image.convert('RGBA')
|
| |
|
| | width, height = image.size
|
| | draw = ImageDraw.Draw(image)
|
| |
|
| | try:
|
| | font1 = ImageFont.truetype(font_path, font_size_line1)
|
| | except IOError:
|
| | print(f"警告:找不到字体文件 {font_path}。将使用默认字体。")
|
| | font1 = ImageFont.load_default()
|
| |
|
| | try:
|
| | font2 = ImageFont.truetype(font_path, font_size_line2)
|
| | except IOError:
|
| | font2 = ImageFont.load_default()
|
| |
|
| | bbox1 = draw.textbbox((0, 0), watermark_text_line1, font=font1)
|
| | text_width_line1 = bbox1[2] - bbox1[0]
|
| | text_height_line1 = bbox1[3] - bbox1[1]
|
| |
|
| | bbox2 = draw.textbbox((0, 0), watermark_text_line2, font=font2)
|
| | text_width_line2 = bbox2[2] - bbox2[0]
|
| | text_height_line2 = bbox2[3] - bbox2[1]
|
| |
|
| | margin = 20
|
| | line_spacing = 10
|
| |
|
| | y_line2 = height - text_height_line2 - margin
|
| | x_line2 = width - text_width_line2 - margin
|
| |
|
| | y_line1 = y_line2 - text_height_line1 - line_spacing
|
| | x_line1 = width - text_width_line1 - margin
|
| |
|
| |
|
| |
|
| |
|
| | white_color = (255, 255, 255, 255)
|
| |
|
| | draw.text((x_line1, y_line1), watermark_text_line1, font=font1, fill=white_color)
|
| | draw.text((x_line2, y_line2), watermark_text_line2, font=font2, fill=white_color)
|
| |
|
| | return image
|
| |
|
| |
|
| |
|
| |
|
| | class WatermarkNode:
|
| |
|
| | @classmethod
|
| | def INPUT_TYPES(cls):
|
| | """定义节点的输入"""
|
| | return {
|
| | "required": {
|
| | "image": ("IMAGE",),
|
| | "font_path": ("STRING", {
|
| | "multiline": False,
|
| |
|
| |
|
| | "default": "D:\\ComfyUI_windows_portable\\ComfyUI\\Fonts\\Iansui-Regular.ttf"
|
| | }),
|
| | "text_line1": ("STRING", {"multiline": False, "default": "妃妃"}),
|
| | "font_size_line1": ("INT", {"default": 40, "min": 1, "max": 1024, "step": 1}),
|
| | "text_line2": ("STRING", {"multiline": False, "default": "aiFeiFei"}),
|
| | "font_size_line2": ("INT", {"default": 30, "min": 1, "max": 1024, "step": 1}),
|
| | }
|
| | }
|
| |
|
| | RETURN_TYPES = ("IMAGE",)
|
| | FUNCTION = "apply_watermark"
|
| | CATEGORY = "Image/Post-Processing"
|
| |
|
| | def apply_watermark(self, image, font_path, text_line1, font_size_line1, text_line2, font_size_line2):
|
| | """节点的核心执行逻辑"""
|
| |
|
| |
|
| |
|
| |
|
| | watermarked_images = []
|
| | for i in range(image.shape[0]):
|
| |
|
| | img_tensor = image[i]
|
| |
|
| |
|
| | img_np = np.clip(255. * img_tensor.cpu().numpy(), 0, 255).astype(np.uint8)
|
| |
|
| |
|
| | pil_image = Image.fromarray(img_np)
|
| |
|
| |
|
| | pil_image_watermarked = add_watermark(
|
| | pil_image,
|
| | font_path,
|
| | text_line1,
|
| | font_size_line1,
|
| | text_line2,
|
| | font_size_line2
|
| | )
|
| |
|
| |
|
| | img_np_watermarked = np.array(pil_image_watermarked).astype(np.float32)
|
| |
|
| |
|
| | img_tensor_watermarked = torch.from_numpy(img_np_watermarked / 255.0)
|
| |
|
| | watermarked_images.append(img_tensor_watermarked)
|
| |
|
| |
|
| | final_tensor = torch.stack(watermarked_images)
|
| |
|
| | return (final_tensor,)
|
| |
|
| |
|
| |
|
| |
|
| | NODE_CLASS_MAPPINGS = {
|
| | "WatermarkNode": WatermarkNode
|
| | }
|
| |
|
| | NODE_DISPLAY_NAME_MAPPINGS = {
|
| | "WatermarkNode": "图像水印 (Watermark)"
|
| | }
|
| |
|