TheEeeeLin commited on
Commit
929aa8b
1 Parent(s): 2c368dd
.gitattributes CHANGED
@@ -1 +1,2 @@
1
  hivision/creator/weights/birefnet-v1-lite.onnx filter=lfs diff=lfs merge=lfs -text
 
 
1
  hivision/creator/weights/birefnet-v1-lite.onnx filter=lfs diff=lfs merge=lfs -text
2
+ hivision/plugin/font/青鸟华光简琥珀.ttf filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -12,6 +12,9 @@ import pathlib
12
  import numpy as np
13
  from demo.utils import csv_to_size_list
14
  import argparse
 
 
 
15
 
16
  # 获取尺寸列表
17
  root_dir = os.path.dirname(os.path.abspath(__file__))
@@ -60,6 +63,13 @@ def idphoto_inference(
60
  custom_image_kb,
61
  language,
62
  matting_model_option,
 
 
 
 
 
 
 
63
  face_detect_option,
64
  head_measure_ratio=0.2,
65
  # head_height_ratio=0.45,
@@ -234,27 +244,49 @@ def idphoto_inference(
234
  )
235
  )
236
 
 
237
  if (
238
  idphoto_json["size_mode"]
239
  == text_lang_map[language]["Only Change Background"]
240
  ):
241
  result_layout_image = gr.update(visible=False)
 
242
  else:
243
  typography_arr, typography_rotate = generate_layout_photo(
244
  input_height=idphoto_json["size"][0],
245
  input_width=idphoto_json["size"][1],
246
  )
247
 
248
- result_layout_image = gr.update(
249
- value=generate_layout_image(
250
- result_image_standard,
251
- typography_arr,
252
- typography_rotate,
253
- height=idphoto_json["size"][0],
254
- width=idphoto_json["size"][1],
255
- ),
256
- visible=True,
257
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
  # 如果输出 KB 大小选择的是自定义
260
  if idphoto_json["custom_image_kb"]:
@@ -272,6 +304,27 @@ def idphoto_inference(
272
  else:
273
  output_image_path = None
274
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  if output_image_path:
276
  result_message = {
277
  img_output_standard: result_image_standard,
@@ -292,6 +345,26 @@ def idphoto_inference(
292
  return result_message
293
 
294
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  if __name__ == "__main__":
296
  argparser = argparse.ArgumentParser()
297
  argparser.add_argument(
@@ -332,6 +405,9 @@ if __name__ == "__main__":
332
  colors_CN = ["蓝色", "白色", "红色", "黑色", "深蓝色", "自定义底色"]
333
  colors_EN = ["Blue", "White", "Red", "Black", "Dark blue", "Custom Color"]
334
 
 
 
 
335
  render_CN = ["纯色", "上下渐变 (白)", "中心渐变 (白)"]
336
  render_EN = ["Solid Color", "Up-Down Gradient (White)", "Center Gradient (White)"]
337
 
@@ -475,6 +551,90 @@ if __name__ == "__main__":
475
  interactive=True,
476
  )
477
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
  img_but = gr.Button("开始制作")
479
 
480
  # 案例图片
@@ -548,6 +708,16 @@ if __name__ == "__main__":
548
  top_distance_option: gr.update(label="头距顶距离"),
549
  key_parameter_tab: gr.update(label="核心参数"),
550
  advance_parameter_tab: gr.update(label="高级参数"),
 
 
 
 
 
 
 
 
 
 
551
  }
552
 
553
  elif language == "English":
@@ -592,6 +762,16 @@ if __name__ == "__main__":
592
  top_distance_option: gr.update(label="Top distance"),
593
  key_parameter_tab: gr.update(label="Key Parameters"),
594
  advance_parameter_tab: gr.update(label="Advance Parameters"),
 
 
 
 
 
 
 
 
 
 
595
  }
596
 
597
  def change_color(colors):
@@ -652,6 +832,14 @@ if __name__ == "__main__":
652
  top_distance_option,
653
  key_parameter_tab,
654
  advance_parameter_tab,
 
 
 
 
 
 
 
 
655
  ],
656
  )
657
 
@@ -686,6 +874,13 @@ if __name__ == "__main__":
686
  custom_image_kb_size,
687
  language_options,
688
  matting_model_options,
 
 
 
 
 
 
 
689
  face_detect_model_options,
690
  head_measure_ratio_option,
691
  top_distance_option,
 
12
  import numpy as np
13
  from demo.utils import csv_to_size_list
14
  import argparse
15
+ from PIL import Image
16
+ from hivision.plugin.watermark import Watermarker, WatermarkerStyles
17
+
18
 
19
  # 获取尺寸列表
20
  root_dir = os.path.dirname(os.path.abspath(__file__))
 
63
  custom_image_kb,
64
  language,
65
  matting_model_option,
66
+ watermark_option,
67
+ watermark_text,
68
+ watermark_text_color,
69
+ watermark_text_size,
70
+ watermark_text_opacity,
71
+ watermark_text_angle,
72
+ watermark_text_space,
73
  face_detect_option,
74
  head_measure_ratio=0.2,
75
  # head_height_ratio=0.45,
 
244
  )
245
  )
246
 
247
+ # 如果只换底,就不生成排版照
248
  if (
249
  idphoto_json["size_mode"]
250
  == text_lang_map[language]["Only Change Background"]
251
  ):
252
  result_layout_image = gr.update(visible=False)
253
+ # 如果是尺寸列表或自定义尺寸,则生成排版照
254
  else:
255
  typography_arr, typography_rotate = generate_layout_photo(
256
  input_height=idphoto_json["size"][0],
257
  input_width=idphoto_json["size"][1],
258
  )
259
 
260
+ if watermark_option == "添加" or watermark_option == "Add":
261
+ result_layout_image = gr.update(
262
+ value=generate_layout_image(
263
+ add_watermark(
264
+ image=result_image_standard,
265
+ text=watermark_text,
266
+ size=watermark_text_size,
267
+ opacity=watermark_text_opacity,
268
+ angle=watermark_text_angle,
269
+ space=watermark_text_space,
270
+ color=watermark_text_color,
271
+ ),
272
+ typography_arr,
273
+ typography_rotate,
274
+ height=idphoto_json["size"][0],
275
+ width=idphoto_json["size"][1],
276
+ ),
277
+ visible=True,
278
+ )
279
+ else:
280
+ result_layout_image = gr.update(
281
+ value=generate_layout_image(
282
+ result_image_standard,
283
+ typography_arr,
284
+ typography_rotate,
285
+ height=idphoto_json["size"][0],
286
+ width=idphoto_json["size"][1],
287
+ ),
288
+ visible=True,
289
+ )
290
 
291
  # 如果输出 KB 大小选择的是自定义
292
  if idphoto_json["custom_image_kb"]:
 
304
  else:
305
  output_image_path = None
306
 
307
+ # Add watermark if selected
308
+ if watermark_option == "添加" or watermark_option == "Add":
309
+ result_image_standard = add_watermark(
310
+ image=result_image_standard,
311
+ text=watermark_text,
312
+ size=watermark_text_size,
313
+ opacity=watermark_text_opacity,
314
+ angle=watermark_text_angle,
315
+ space=watermark_text_space,
316
+ color=watermark_text_color,
317
+ )
318
+ result_image_hd = add_watermark(
319
+ image=result_image_hd,
320
+ text=watermark_text,
321
+ size=watermark_text_size,
322
+ opacity=watermark_text_opacity,
323
+ angle=watermark_text_angle,
324
+ space=watermark_text_space,
325
+ color=watermark_text_color,
326
+ )
327
+
328
  if output_image_path:
329
  result_message = {
330
  img_output_standard: result_image_standard,
 
345
  return result_message
346
 
347
 
348
+ # Add this function to handle watermark addition
349
+ def add_watermark(
350
+ image: np.ndarray, text, size=50, opacity=0.5, angle=45, color="#8B8B1B", space=75
351
+ ):
352
+ image = Image.fromarray(image)
353
+ # 创建 Watermarker 实例
354
+ watermarker = Watermarker(
355
+ input_image=image,
356
+ text=text,
357
+ style=WatermarkerStyles.STRIPED,
358
+ angle=angle,
359
+ color=color,
360
+ opacity=opacity,
361
+ size=size,
362
+ space=space,
363
+ )
364
+ # 返回带水印的图片
365
+ return np.array(watermarker.image.convert("RGB"))
366
+
367
+
368
  if __name__ == "__main__":
369
  argparser = argparse.ArgumentParser()
370
  argparser.add_argument(
 
405
  colors_CN = ["蓝色", "白色", "红色", "黑色", "深蓝色", "自定义底色"]
406
  colors_EN = ["Blue", "White", "Red", "Black", "Dark blue", "Custom Color"]
407
 
408
+ watermark_CN = ["不添加", "添加"]
409
+ watermark_EN = ["Not Add", "Add"]
410
+
411
  render_CN = ["纯色", "上下渐变 (白)", "中心渐变 (白)"]
412
  render_EN = ["Solid Color", "Up-Down Gradient (White)", "Center Gradient (White)"]
413
 
 
551
  interactive=True,
552
  )
553
 
554
+ with gr.Tab("水印") as watermark_parameter_tab:
555
+ # 左: 水印
556
+ watermark_options = gr.Radio(
557
+ choices=watermark_CN,
558
+ label="水印",
559
+ value="不添加",
560
+ elem_id="watermark",
561
+ )
562
+
563
+ with gr.Row():
564
+ # 左:水印文字
565
+ watermark_text_options = gr.Text(
566
+ max_length=10,
567
+ label="水印文字",
568
+ value="Hello",
569
+ placeholder="请输入水印文字(最多10个字符)",
570
+ elem_id="watermark_text",
571
+ interactive=False,
572
+ )
573
+ # 水印颜色
574
+ watermark_text_color = gr.ColorPicker(
575
+ label="水印颜色",
576
+ interactive=False,
577
+ value="#FFFFFF",
578
+ )
579
+
580
+ # 文字大小
581
+ watermark_text_size = gr.Slider(
582
+ minimum=10,
583
+ maximum=100,
584
+ value=20,
585
+ label="文字大小",
586
+ interactive=False,
587
+ step=1,
588
+ )
589
+
590
+ # 文字透明度
591
+ watermark_text_opacity = gr.Slider(
592
+ minimum=0,
593
+ maximum=1,
594
+ value=0.15,
595
+ label="水印透明度",
596
+ interactive=False,
597
+ step=0.01,
598
+ )
599
+
600
+ # 文字角度
601
+ watermark_text_angle = gr.Slider(
602
+ minimum=0,
603
+ maximum=360,
604
+ value=30,
605
+ label="水印角度",
606
+ interactive=False,
607
+ step=1,
608
+ )
609
+
610
+ # 文字间距
611
+ watermark_text_space = gr.Slider(
612
+ minimum=10,
613
+ maximum=200,
614
+ value=25,
615
+ label="水印间距",
616
+ interactive=False,
617
+ step=1,
618
+ )
619
+
620
+ def update_watermark_text_visibility(choice):
621
+ return [
622
+ gr.update(interactive=(choice == "添加" or choice == "Add"))
623
+ ] * 6
624
+
625
+ watermark_options.change(
626
+ fn=update_watermark_text_visibility,
627
+ inputs=[watermark_options],
628
+ outputs=[
629
+ watermark_text_options,
630
+ watermark_text_color,
631
+ watermark_text_size,
632
+ watermark_text_opacity,
633
+ watermark_text_angle,
634
+ watermark_text_space,
635
+ ],
636
+ )
637
+
638
  img_but = gr.Button("开始制作")
639
 
640
  # 案例图片
 
708
  top_distance_option: gr.update(label="头距顶距离"),
709
  key_parameter_tab: gr.update(label="核心参数"),
710
  advance_parameter_tab: gr.update(label="高级参数"),
711
+ watermark_parameter_tab: gr.update(label="水印"),
712
+ watermark_text_options: gr.update(label="水印文字"),
713
+ watermark_text_color: gr.update(label="水印颜色"),
714
+ watermark_text_size: gr.update(label="文字大小"),
715
+ watermark_text_opacity: gr.update(label="水印透明度"),
716
+ watermark_text_angle: gr.update(label="水印角度"),
717
+ watermark_text_space: gr.update(label="水印间距"),
718
+ watermark_options: gr.update(
719
+ label="水印", value="不添加", choices=watermark_CN
720
+ ),
721
  }
722
 
723
  elif language == "English":
 
762
  top_distance_option: gr.update(label="Top distance"),
763
  key_parameter_tab: gr.update(label="Key Parameters"),
764
  advance_parameter_tab: gr.update(label="Advance Parameters"),
765
+ watermark_parameter_tab: gr.update(label="Watermark"),
766
+ watermark_text_options: gr.update(label="Text"),
767
+ watermark_text_color: gr.update(label="Color"),
768
+ watermark_text_size: gr.update(label="Size"),
769
+ watermark_text_opacity: gr.update(label="Opacity"),
770
+ watermark_text_angle: gr.update(label="Angle"),
771
+ watermark_text_space: gr.update(label="Space"),
772
+ watermark_options: gr.update(
773
+ label="Watermark", value="Not Add", choices=watermark_EN
774
+ ),
775
  }
776
 
777
  def change_color(colors):
 
832
  top_distance_option,
833
  key_parameter_tab,
834
  advance_parameter_tab,
835
+ watermark_parameter_tab,
836
+ watermark_text_options,
837
+ watermark_text_color,
838
+ watermark_text_size,
839
+ watermark_text_opacity,
840
+ watermark_text_angle,
841
+ watermark_text_space,
842
+ watermark_options,
843
  ],
844
  )
845
 
 
874
  custom_image_kb_size,
875
  language_options,
876
  matting_model_options,
877
+ watermark_options,
878
+ watermark_text_options,
879
+ watermark_text_color,
880
+ watermark_text_size,
881
+ watermark_text_opacity,
882
+ watermark_text_angle,
883
+ watermark_text_space,
884
  face_detect_model_options,
885
  head_measure_ratio_option,
886
  top_distance_option,
hivision/.gitattributes DELETED
@@ -1,37 +0,0 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
36
- assets/demoImage.png filter=lfs diff=lfs merge=lfs -text
37
- hivision/creator/weights/rmbg-1.4.onnx filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hivision/creator/layout_calculator.py CHANGED
@@ -128,6 +128,8 @@ def generate_layout_image(
128
  input_image = cv2.resize(input_image, (width, height))
129
  if typography_rotate:
130
  input_image = cv2.transpose(input_image)
 
 
131
  height, width = width, height
132
  for arr in typography_arr:
133
  locate_x, locate_y = arr[0], arr[1]
 
128
  input_image = cv2.resize(input_image, (width, height))
129
  if typography_rotate:
130
  input_image = cv2.transpose(input_image)
131
+ input_image = cv2.flip(input_image, 0) # 0 表示垂直镜像
132
+
133
  height, width = width, height
134
  for arr in typography_arr:
135
  locate_x, locate_y = arr[0], arr[1]
hivision/plugin/font/.gitkeep ADDED
File without changes
hivision/plugin/font/青鸟华光简琥珀.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d56e625f65cd5c73485e31831a46c612a0b680d2db103886527086fef3d74256
3
+ size 4247512
hivision/plugin/watermark.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Reference: https://gist.github.com/Deali-Axy/e22ea79bfbe785f9017b2e3cd7fdb3eb
3
+ """
4
+
5
+ import enum
6
+ import os
7
+ import math
8
+ import textwrap
9
+ from PIL import Image, ImageFont, ImageDraw, ImageEnhance, ImageChops
10
+ import os
11
+
12
+ base_path = os.path.abspath(os.path.dirname(__file__))
13
+
14
+
15
+ class WatermarkerStyles(enum.Enum):
16
+ """水印样式"""
17
+
18
+ STRIPED = 1 # 斜向重复
19
+ CENTRAL = 2 # 居中
20
+
21
+
22
+ class Watermarker(object):
23
+ """图片水印工具"""
24
+
25
+ def __init__(
26
+ self,
27
+ input_image: Image.Image,
28
+ text: str,
29
+ style: WatermarkerStyles,
30
+ angle=30,
31
+ color="#8B8B1B",
32
+ font_file="青鸟华光简琥珀.ttf",
33
+ opacity=0.15,
34
+ size=50,
35
+ space=75,
36
+ chars_per_line=8,
37
+ font_height_crop=1.2,
38
+ ):
39
+ """_summary_
40
+
41
+ Parameters
42
+ ----------
43
+ input_image : Image.Image
44
+ PIL图片对象
45
+ text : str
46
+ 水印文字
47
+ style : WatermarkerStyles
48
+ 水印样式
49
+ angle : int, optional
50
+ 水印角度, by default 30
51
+ color : str, optional
52
+ 水印颜色, by default "#8B8B1B"
53
+ font_file : str, optional
54
+ 字体文件, by default "青鸟华光简琥珀.ttf"
55
+ font_height_crop : float, optional
56
+ 字体高度裁剪比例, by default 1.2
57
+ opacity : float, optional
58
+ 水印透明度, by default 0.15
59
+ size : int, optional
60
+ 字体大小, by default 50
61
+ space : int, optional
62
+ 水印间距, by default 75
63
+ chars_per_line : int, optional
64
+ 每行字符数, by default 8
65
+ """
66
+ self.input_image = input_image
67
+ self.text = text
68
+ self.style = style
69
+ self.angle = angle
70
+ self.color = color
71
+ self.font_file = os.path.join(base_path, "font", font_file)
72
+ self.font_height_crop = font_height_crop
73
+ self.opacity = opacity
74
+ self.size = size
75
+ self.space = space
76
+ self.chars_per_line = chars_per_line
77
+ self._result_image = None
78
+
79
+ @staticmethod
80
+ def set_image_opacity(image: Image, opacity: float):
81
+ alpha = image.split()[3]
82
+ alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
83
+ image.putalpha(alpha)
84
+ return image
85
+
86
+ @staticmethod
87
+ def crop_image_edge(image: Image):
88
+ bg = Image.new(mode="RGBA", size=image.size)
89
+ diff = ImageChops.difference(image, bg)
90
+ bbox = diff.getbbox()
91
+ if bbox:
92
+ return image.crop(bbox)
93
+ return image
94
+
95
+ def _add_mark_striped(self):
96
+ origin_image = self.input_image.convert("RGBA")
97
+ width = len(self.text) * self.size
98
+ height = round(self.size * self.font_height_crop)
99
+ watermark_image = Image.new(mode="RGBA", size=(width, height))
100
+ draw_table = ImageDraw.Draw(watermark_image)
101
+ draw_table.text(
102
+ (0, 0),
103
+ self.text,
104
+ fill=self.color,
105
+ font=ImageFont.truetype(self.font_file, size=self.size),
106
+ )
107
+ watermark_image = Watermarker.crop_image_edge(watermark_image)
108
+ Watermarker.set_image_opacity(watermark_image, self.opacity)
109
+
110
+ c = int(math.sqrt(origin_image.size[0] ** 2 + origin_image.size[1] ** 2))
111
+ watermark_mask = Image.new(mode="RGBA", size=(c, c))
112
+ y, idx = 0, 0
113
+ while y < c:
114
+ x = -int((watermark_image.size[0] + self.space) * 0.5 * idx)
115
+ idx = (idx + 1) % 2
116
+ while x < c:
117
+ watermark_mask.paste(watermark_image, (x, y))
118
+ x += watermark_image.size[0] + self.space
119
+ y += watermark_image.size[1] + self.space
120
+
121
+ watermark_mask = watermark_mask.rotate(self.angle)
122
+ origin_image.paste(
123
+ watermark_mask,
124
+ (int((origin_image.size[0] - c) / 2), int((origin_image.size[1] - c) / 2)),
125
+ mask=watermark_mask.split()[3],
126
+ )
127
+ return origin_image
128
+
129
+ def _add_mark_central(self):
130
+ origin_image = self.input_image.convert("RGBA")
131
+ text_lines = textwrap.wrap(self.text, width=self.chars_per_line)
132
+ text = "\n".join(text_lines)
133
+ width = len(text) * self.size
134
+ height = round(self.size * self.font_height_crop * len(text_lines))
135
+ watermark_image = Image.new(mode="RGBA", size=(width, height))
136
+ draw_table = ImageDraw.Draw(watermark_image)
137
+ draw_table.text(
138
+ (0, 0),
139
+ text,
140
+ fill=self.color,
141
+ font=ImageFont.truetype(self.font_file, size=self.size),
142
+ )
143
+ watermark_image = Watermarker.crop_image_edge(watermark_image)
144
+ Watermarker.set_image_opacity(watermark_image, self.opacity)
145
+
146
+ c = int(math.sqrt(origin_image.size[0] ** 2 + origin_image.size[1] ** 2))
147
+ watermark_mask = Image.new(mode="RGBA", size=(c, c))
148
+ watermark_mask.paste(
149
+ watermark_image,
150
+ (
151
+ int((watermark_mask.width - watermark_image.width) / 2),
152
+ int((watermark_mask.height - watermark_image.height) / 2),
153
+ ),
154
+ )
155
+ watermark_mask = watermark_mask.rotate(self.angle)
156
+
157
+ origin_image.paste(
158
+ watermark_mask,
159
+ (
160
+ int((origin_image.width - watermark_mask.width) / 2),
161
+ int((origin_image.height - watermark_mask.height) / 2),
162
+ ),
163
+ mask=watermark_mask.split()[3],
164
+ )
165
+ return origin_image
166
+
167
+ @property
168
+ def image(self):
169
+ if not self._result_image:
170
+ if self.style == WatermarkerStyles.STRIPED:
171
+ self._result_image = self._add_mark_striped()
172
+ elif self.style == WatermarkerStyles.CENTRAL:
173
+ self._result_image = self._add_mark_central()
174
+ return self._result_image
175
+
176
+ def save(self, file_path: str, image_format: str = "png"):
177
+ with open(file_path, "wb") as f:
178
+ self.image.save(f, image_format)
179
+
180
+
181
+ # Gradio 接口
182
+ def watermark_image(
183
+ image,
184
+ text,
185
+ style,
186
+ angle,
187
+ color,
188
+ opacity,
189
+ size,
190
+ space,
191
+ ):
192
+ # 创建 Watermarker 实例
193
+ watermarker = Watermarker(
194
+ input_image=image,
195
+ text=text,
196
+ style=(
197
+ WatermarkerStyles.STRIPED
198
+ if style == "STRIPED"
199
+ else WatermarkerStyles.CENTRAL
200
+ ),
201
+ angle=angle,
202
+ color=color,
203
+ opacity=opacity,
204
+ size=size,
205
+ space=space,
206
+ )
207
+
208
+ # 返回带水印的图片
209
+ return watermarker.image
210
+
211
+
212
+ if __name__ == "__main__":
213
+ import gradio as gr
214
+
215
+ iface = gr.Interface(
216
+ fn=watermark_image,
217
+ inputs=[
218
+ gr.Image(type="pil", label="上传图片", height=400),
219
+ gr.Textbox(label="水印文字"),
220
+ gr.Radio(choices=["STRIPED", "CENTRAL"], label="水印样式"),
221
+ gr.Slider(minimum=0, maximum=360, value=30, label="水印角度"),
222
+ gr.ColorPicker(label="水印颜色"),
223
+ gr.Slider(minimum=0, maximum=1, value=0.15, label="水印透明度"),
224
+ gr.Slider(minimum=10, maximum=100, value=50, label="字体大小"),
225
+ gr.Slider(minimum=10, maximum=200, value=75, label="水印间距"),
226
+ ],
227
+ outputs=gr.Image(type="pil", label="带水印的图片", height=400),
228
+ title="图片水印工具",
229
+ description="上传一张图片,添加水印并下载。",
230
+ )
231
+
232
+ iface.launch()