Spaces:
Running
Running
File size: 7,440 Bytes
929aa8b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
"""
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()
|