TheEeeeLin's picture
lfs
929aa8b
raw
history blame
7.44 kB
"""
Reference: https://gist.github.com/Deali-Axy/e22ea79bfbe785f9017b2e3cd7fdb3eb
"""
import enum
import os
import math
import textwrap
from PIL import Image, ImageFont, ImageDraw, ImageEnhance, ImageChops
import os
base_path = os.path.abspath(os.path.dirname(__file__))
class WatermarkerStyles(enum.Enum):
"""水印样式"""
STRIPED = 1 # 斜向重复
CENTRAL = 2 # 居中
class Watermarker(object):
"""图片水印工具"""
def __init__(
self,
input_image: Image.Image,
text: str,
style: WatermarkerStyles,
angle=30,
color="#8B8B1B",
font_file="青鸟华光简琥珀.ttf",
opacity=0.15,
size=50,
space=75,
chars_per_line=8,
font_height_crop=1.2,
):
"""_summary_
Parameters
----------
input_image : Image.Image
PIL图片对象
text : str
水印文字
style : WatermarkerStyles
水印样式
angle : int, optional
水印角度, by default 30
color : str, optional
水印颜色, by default "#8B8B1B"
font_file : str, optional
字体文件, by default "青鸟华光简琥珀.ttf"
font_height_crop : float, optional
字体高度裁剪比例, by default 1.2
opacity : float, optional
水印透明度, by default 0.15
size : int, optional
字体大小, by default 50
space : int, optional
水印间距, by default 75
chars_per_line : int, optional
每行字符数, by default 8
"""
self.input_image = input_image
self.text = text
self.style = style
self.angle = angle
self.color = color
self.font_file = os.path.join(base_path, "font", font_file)
self.font_height_crop = font_height_crop
self.opacity = opacity
self.size = size
self.space = space
self.chars_per_line = chars_per_line
self._result_image = None
@staticmethod
def set_image_opacity(image: Image, opacity: float):
alpha = image.split()[3]
alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
image.putalpha(alpha)
return image
@staticmethod
def crop_image_edge(image: Image):
bg = Image.new(mode="RGBA", size=image.size)
diff = ImageChops.difference(image, bg)
bbox = diff.getbbox()
if bbox:
return image.crop(bbox)
return image
def _add_mark_striped(self):
origin_image = self.input_image.convert("RGBA")
width = len(self.text) * self.size
height = round(self.size * self.font_height_crop)
watermark_image = Image.new(mode="RGBA", size=(width, height))
draw_table = ImageDraw.Draw(watermark_image)
draw_table.text(
(0, 0),
self.text,
fill=self.color,
font=ImageFont.truetype(self.font_file, size=self.size),
)
watermark_image = Watermarker.crop_image_edge(watermark_image)
Watermarker.set_image_opacity(watermark_image, self.opacity)
c = int(math.sqrt(origin_image.size[0] ** 2 + origin_image.size[1] ** 2))
watermark_mask = Image.new(mode="RGBA", size=(c, c))
y, idx = 0, 0
while y < c:
x = -int((watermark_image.size[0] + self.space) * 0.5 * idx)
idx = (idx + 1) % 2
while x < c:
watermark_mask.paste(watermark_image, (x, y))
x += watermark_image.size[0] + self.space
y += watermark_image.size[1] + self.space
watermark_mask = watermark_mask.rotate(self.angle)
origin_image.paste(
watermark_mask,
(int((origin_image.size[0] - c) / 2), int((origin_image.size[1] - c) / 2)),
mask=watermark_mask.split()[3],
)
return origin_image
def _add_mark_central(self):
origin_image = self.input_image.convert("RGBA")
text_lines = textwrap.wrap(self.text, width=self.chars_per_line)
text = "\n".join(text_lines)
width = len(text) * self.size
height = round(self.size * self.font_height_crop * len(text_lines))
watermark_image = Image.new(mode="RGBA", size=(width, height))
draw_table = ImageDraw.Draw(watermark_image)
draw_table.text(
(0, 0),
text,
fill=self.color,
font=ImageFont.truetype(self.font_file, size=self.size),
)
watermark_image = Watermarker.crop_image_edge(watermark_image)
Watermarker.set_image_opacity(watermark_image, self.opacity)
c = int(math.sqrt(origin_image.size[0] ** 2 + origin_image.size[1] ** 2))
watermark_mask = Image.new(mode="RGBA", size=(c, c))
watermark_mask.paste(
watermark_image,
(
int((watermark_mask.width - watermark_image.width) / 2),
int((watermark_mask.height - watermark_image.height) / 2),
),
)
watermark_mask = watermark_mask.rotate(self.angle)
origin_image.paste(
watermark_mask,
(
int((origin_image.width - watermark_mask.width) / 2),
int((origin_image.height - watermark_mask.height) / 2),
),
mask=watermark_mask.split()[3],
)
return origin_image
@property
def image(self):
if not self._result_image:
if self.style == WatermarkerStyles.STRIPED:
self._result_image = self._add_mark_striped()
elif self.style == WatermarkerStyles.CENTRAL:
self._result_image = self._add_mark_central()
return self._result_image
def save(self, file_path: str, image_format: str = "png"):
with open(file_path, "wb") as f:
self.image.save(f, image_format)
# Gradio 接口
def watermark_image(
image,
text,
style,
angle,
color,
opacity,
size,
space,
):
# 创建 Watermarker 实例
watermarker = Watermarker(
input_image=image,
text=text,
style=(
WatermarkerStyles.STRIPED
if style == "STRIPED"
else WatermarkerStyles.CENTRAL
),
angle=angle,
color=color,
opacity=opacity,
size=size,
space=space,
)
# 返回带水印的图片
return watermarker.image
if __name__ == "__main__":
import gradio as gr
iface = gr.Interface(
fn=watermark_image,
inputs=[
gr.Image(type="pil", label="上传图片", height=400),
gr.Textbox(label="水印文字"),
gr.Radio(choices=["STRIPED", "CENTRAL"], label="水印样式"),
gr.Slider(minimum=0, maximum=360, value=30, label="水印角度"),
gr.ColorPicker(label="水印颜色"),
gr.Slider(minimum=0, maximum=1, value=0.15, label="水印透明度"),
gr.Slider(minimum=10, maximum=100, value=50, label="字体大小"),
gr.Slider(minimum=10, maximum=200, value=75, label="水印间距"),
],
outputs=gr.Image(type="pil", label="带水印的图片", height=400),
title="图片水印工具",
description="上传一张图片,添加水印并下载。",
)
iface.launch()