Spaces:
Running
Running
TheEeeeLin
commited on
Commit
•
929aa8b
1
Parent(s):
2c368dd
lfs
Browse files- .gitattributes +1 -0
- app.py +205 -10
- hivision/.gitattributes +0 -37
- hivision/creator/layout_calculator.py +2 -0
- hivision/plugin/font/.gitkeep +0 -0
- hivision/plugin/font/青鸟华光简琥珀.ttf +3 -0
- hivision/plugin/watermark.py +232 -0
.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 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
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()
|