TheEeeeLin commited on
Commit
402b504
1 Parent(s): a5eebe4

update retinaface

Browse files
.gitattributes CHANGED
@@ -3,3 +3,4 @@ hivision/plugin/font/青鸟华光简琥珀.ttf filter=lfs diff=lfs merge=lfs -te
3
  hivision/creator/weights/hivision_modnet.onnx filter=lfs diff=lfs merge=lfs -text
4
  hivision/creator/weights/modnet_photographic_portrait_matting.onnx filter=lfs diff=lfs merge=lfs -text
5
  hivision/creator/weights/rmbg-1.4.onnx filter=lfs diff=lfs merge=lfs -text
 
 
3
  hivision/creator/weights/hivision_modnet.onnx filter=lfs diff=lfs merge=lfs -text
4
  hivision/creator/weights/modnet_photographic_portrait_matting.onnx filter=lfs diff=lfs merge=lfs -text
5
  hivision/creator/weights/rmbg-1.4.onnx filter=lfs diff=lfs merge=lfs -text
6
+ hivision/creator/retinaface/weights/retinaface-resnet50.onnx filter=lfs diff=lfs merge=lfs -text
.gitignore CHANGED
@@ -17,6 +17,7 @@ build
17
  test/temp/*
18
  !test/temp/.gitkeep
19
  !hivision/creator/weights/rmbg-1.4.onnx
20
- !hivision/creator/weights/birefnet-v1-lite.onnx
 
21
 
22
  .python-version
 
17
  test/temp/*
18
  !test/temp/.gitkeep
19
  !hivision/creator/weights/rmbg-1.4.onnx
20
+ !hivision/creator/weights/birefnet-v1-lite.onnx
21
+ !hivision/creator/retinaface/weights/retinaface-resnet50.onnx
22
 
23
  .python-version
app.py CHANGED
@@ -1,369 +1,32 @@
1
- import os
2
- import gradio as gr
3
- from hivision import IDCreator
4
- from hivision.error import FaceError, APIError
5
- from hivision.utils import add_background, resize_image_to_kb
6
- from hivision.creator.layout_calculator import (
7
- generate_layout_photo,
8
- generate_layout_image,
9
- )
10
- from hivision.creator.choose_handler import choose_handler
11
- import pathlib
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__))
21
- size_list_dict_CN = csv_to_size_list(os.path.join(root_dir, "demo/size_list_CN.csv"))
22
- size_list_dict_EN = csv_to_size_list(os.path.join(root_dir, "demo/size_list_EN.csv"))
23
-
24
- color_list_dict_CN = {
25
- "蓝色": (86, 140, 212),
26
- "白色": (255, 255, 255),
27
- "红色": (233, 51, 35),
28
- "黑色": (0, 0, 0),
29
- "深蓝色": (69, 98, 148),
30
- }
31
-
32
- color_list_dict_EN = {
33
- "Blue": (86, 140, 212),
34
- "White": (255, 255, 255),
35
- "Red": (233, 51, 35),
36
- "Black": (0, 0, 0),
37
- "Dark blue": (69, 98, 148),
38
- }
39
-
40
-
41
- # 检测 RGB 是否超出范围,如果超出则约束到 0~255 之间
42
- def range_check(value, min_value=0, max_value=255):
43
- value = int(value)
44
- if value <= min_value:
45
- value = min_value
46
- elif value > max_value:
47
- value = max_value
48
- return value
49
-
50
-
51
- def idphoto_inference(
52
- input_image,
53
- mode_option,
54
- size_list_option,
55
- color_option,
56
- render_option,
57
- image_kb_options,
58
- custom_color_R,
59
- custom_color_G,
60
- custom_color_B,
61
- custom_size_height,
62
- custom_size_width,
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,
76
- top_distance_max=0.12,
77
- top_distance_min=0.10,
78
- ):
79
 
80
- top_distance_min = top_distance_max - 0.02
81
-
82
- idphoto_json = {
83
- "size_mode": mode_option,
84
- "color_mode": color_option,
85
- "render_mode": render_option,
86
- "image_kb_mode": image_kb_options,
87
- }
88
-
89
- text_lang_map = {
90
- "中文": {
91
- "Size List": "尺寸列表",
92
- "Custom Size": "自定义尺寸",
93
- "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.": "宽度应不大于长度;长宽不应小于 100,大于 1800",
94
- "Custom Color": "自定义底色",
95
- "Custom": "自定义",
96
- "The number of faces is not equal to 1, please upload an image with a single face. If the actual number of faces is 1, it may be an issue with the accuracy of the detection model. Please switch to a different face detection model on the left or raise a Github Issue to notify the author.": "人脸数量不等于 1,请上传单张人脸的图像。如果实际人脸数量为 1,可能是检测模型精度的问题,请在左边更换人脸检测模型或给作者提Github Issue。",
97
- "Solid Color": "纯色",
98
- "Up-Down Gradient (White)": "上下渐变 (白)",
99
- "Center Gradient (White)": "中心渐变 (白)",
100
- "Set KB size (Download in the bottom right)": "设置 KB 大小(结果在右边最底的组件下载)",
101
- "Not Set": "不设置",
102
- "Only Change Background": "只换底",
103
- },
104
- "English": {
105
- "Size List": "Size List",
106
- "Custom Size": "Custom Size",
107
- "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.": "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.",
108
- "Custom Color": "Custom Color",
109
- "Custom": "Custom",
110
- "The number of faces is not equal to 1, please upload an image with a single face. If the actual number of faces is 1, it may be an issue with the accuracy of the detection model. Please switch to a different face detection model on the left or raise a Github Issue to notify the author.": "The number of faces is not equal to 1, please upload an image with a single face. If the actual number of faces is 1, it may be an issue with the accuracy of the detection model. Please switch to a different face detection model on the left or raise a Github Issue to notify the author.",
111
- "Solid Color": "Solid Color",
112
- "Up-Down Gradient (White)": "Up-Down Gradient (White)",
113
- "Center Gradient (White)": "Center Gradient (White)",
114
- "Set KB size (Download in the bottom right)": "Set KB size (Download in the bottom right)",
115
- "Not Set": "Not Set",
116
- "Only Change Background": "Only Change Background",
117
- },
118
- }
119
-
120
- # 如果尺寸模式选择的是尺寸列表
121
- if idphoto_json["size_mode"] == text_lang_map[language]["Size List"]:
122
- if language == "中文":
123
- idphoto_json["size"] = size_list_dict_CN[size_list_option]
124
- else:
125
- idphoto_json["size"] = size_list_dict_EN[size_list_option]
126
- # 如果尺寸模式选择的是自定义尺寸
127
- elif idphoto_json["size_mode"] == text_lang_map[language]["Custom Size"]:
128
- id_height = int(custom_size_height)
129
- id_width = int(custom_size_width)
130
- if (
131
- id_height < id_width
132
- or min(id_height, id_width) < 100
133
- or max(id_height, id_width) > 1800
134
- ):
135
- return {
136
- img_output_standard: gr.update(value=None),
137
- img_output_standard_hd: gr.update(value=None),
138
- notification: gr.update(
139
- value=text_lang_map[language][
140
- "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800."
141
- ],
142
- visible=True,
143
- ),
144
- }
145
- idphoto_json["size"] = (id_height, id_width)
146
- else:
147
- idphoto_json["size"] = (None, None)
148
-
149
- # 如果颜色模式选择的是自定义底色
150
- if idphoto_json["color_mode"] == text_lang_map[language]["Custom Color"]:
151
- idphoto_json["color_bgr"] = (
152
- range_check(custom_color_R),
153
- range_check(custom_color_G),
154
- range_check(custom_color_B),
155
- )
156
- else:
157
- if language == "中文":
158
- idphoto_json["color_bgr"] = color_list_dict_CN[color_option]
159
- else:
160
- idphoto_json["color_bgr"] = color_list_dict_EN[color_option]
161
-
162
- # 如果输出 KB 大小选择的是自定义
163
- if idphoto_json["image_kb_mode"] == text_lang_map[language]["Custom"]:
164
- idphoto_json["custom_image_kb"] = custom_image_kb
165
- else:
166
- idphoto_json["custom_image_kb"] = None
167
-
168
- creator = IDCreator()
169
- choose_handler(creator, matting_model_option, face_detect_option)
170
-
171
- change_bg_only = idphoto_json["size_mode"] in ["只换底", "Only Change Background"]
172
- # 生成证件照
173
- try:
174
- result = creator(
175
- input_image,
176
- change_bg_only=change_bg_only,
177
- size=idphoto_json["size"],
178
- head_measure_ratio=head_measure_ratio,
179
- # head_height_ratio=head_height_ratio,
180
- head_top_range=(top_distance_max, top_distance_min),
181
  )
182
- except FaceError:
183
- result_message = {
184
- img_output_standard: gr.update(value=None),
185
- img_output_standard_hd: gr.update(value=None),
186
- img_output_layout: gr.update(visible=False),
187
- notification: gr.update(
188
- value=text_lang_map[language][
189
- "The number of faces is not equal to 1, please upload an image with a single face. If the actual number of faces is 1, it may be an issue with the accuracy of the detection model. Please switch to a different face detection model on the left or raise a Github Issue to notify the author."
190
- ],
191
- visible=True,
192
- ),
193
- }
194
- except APIError as e:
195
- result_message = {
196
- img_output_standard: gr.update(value=None),
197
- img_output_standard_hd: gr.update(value=None),
198
- img_output_layout: gr.update(visible=False),
199
- notification: gr.update(
200
- value=f"Please make sure you have correctly set up the Face++ API Key and Secret.\nAPI Error\nStatus Code is {e.status_code}\nPossible errors are: {e}\n",
201
- visible=True,
202
- ),
203
- }
204
- else:
205
- (result_image_standard, result_image_hd, _, _) = result
206
- if idphoto_json["render_mode"] == text_lang_map[language]["Solid Color"]:
207
- result_image_standard = np.uint8(
208
- add_background(result_image_standard, bgr=idphoto_json["color_bgr"])
209
- )
210
- result_image_hd = np.uint8(
211
- add_background(result_image_hd, bgr=idphoto_json["color_bgr"])
212
- )
213
- elif (
214
- idphoto_json["render_mode"]
215
- == text_lang_map[language]["Up-Down Gradient (White)"]
216
- ):
217
- result_image_standard = np.uint8(
218
- add_background(
219
- result_image_standard,
220
- bgr=idphoto_json["color_bgr"],
221
- mode="updown_gradient",
222
- )
223
- )
224
- result_image_hd = np.uint8(
225
- add_background(
226
- result_image_hd,
227
- bgr=idphoto_json["color_bgr"],
228
- mode="updown_gradient",
229
- )
230
- )
231
- else:
232
- result_image_standard = np.uint8(
233
- add_background(
234
- result_image_standard,
235
- bgr=idphoto_json["color_bgr"],
236
- mode="center_gradient",
237
- )
238
- )
239
- result_image_hd = np.uint8(
240
- add_background(
241
- result_image_hd,
242
- bgr=idphoto_json["color_bgr"],
243
- mode="center_gradient",
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"]:
293
- # 将标准照大小调整至目标大小
294
- print("调整 kb 大小到", idphoto_json["custom_image_kb"], "kb")
295
- # 输出路径为一个根据时间戳 + 哈希值生成的随机文件名
296
- import time
297
-
298
- output_image_path = f"{os.path.join(os.path.dirname(__file__), 'demo/kb_output')}/{int(time.time())}.jpg"
299
- resize_image_to_kb(
300
- result_image_standard,
301
- output_image_path,
302
- idphoto_json["custom_image_kb"],
303
- )
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,
331
- img_output_standard_hd: result_image_hd,
332
- img_output_layout: result_layout_image,
333
- notification: gr.update(visible=False),
334
- file_download: gr.update(visible=True, value=output_image_path),
335
- }
336
- else:
337
- result_message = {
338
- img_output_standard: result_image_standard,
339
- img_output_standard_hd: result_image_hd,
340
- img_output_layout: result_layout_image,
341
- notification: gr.update(visible=False),
342
- file_download: gr.update(visible=False),
343
- }
344
-
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()
@@ -379,525 +42,17 @@ if __name__ == "__main__":
379
  default=None,
380
  help="The root path of the server, default is None (='/'), e.g. '/myapp'",
381
  )
382
-
383
  args = argparser.parse_args()
384
 
385
- language = ["中文", "English"]
386
-
387
- matting_model_list = [
388
- os.path.splitext(file)[0]
389
- for file in os.listdir(os.path.join(root_dir, "hivision/creator/weights"))
390
- if file.endswith(".onnx") or file.endswith(".mnn")
391
- ]
392
- DEFAULT_MATTING_MODEL = "modnet_photographic_portrait_matting"
393
- if DEFAULT_MATTING_MODEL in matting_model_list:
394
- matting_model_list.remove(DEFAULT_MATTING_MODEL)
395
- matting_model_list.insert(0, DEFAULT_MATTING_MODEL)
396
-
397
- face_detect_model_list = ["mtcnn", "face++ (联网API)"]
398
-
399
- size_mode_CN = ["尺寸列表", "只换底", "自定义尺寸"]
400
- size_mode_EN = ["Size List", "Only Change Background", "Custom Size"]
401
-
402
- size_list_CN = list(size_list_dict_CN.keys())
403
- size_list_EN = list(size_list_dict_EN.keys())
404
-
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
-
414
- image_kb_CN = ["不设置", "自定义"]
415
- image_kb_EN = ["Not Set", "Custom"]
416
-
417
- css = """
418
- #col-left {
419
- margin: 0 auto;
420
- max-width: 430px;
421
- }
422
- #col-mid {
423
- margin: 0 auto;
424
- max-width: 430px;
425
- }
426
- #col-right {
427
- margin: 0 auto;
428
- max-width: 430px;
429
- }
430
- #col-showcase {
431
- margin: 0 auto;
432
- max-width: 1100px;
433
- }
434
- #button {
435
- color: blue;
436
- }
437
- """
438
-
439
- def load_description(fp):
440
- with open(fp, "r", encoding="utf-8") as f:
441
- content = f.read()
442
- return content
443
-
444
- demo = gr.Blocks(title="HivisionIDPhotos", css=css)
445
-
446
- with demo:
447
- gr.HTML(load_description(os.path.join(root_dir, "assets/title.md")))
448
- with gr.Row():
449
- # ------------ 左半边 UI ----------------
450
- with gr.Column():
451
- img_input = gr.Image(height=400)
452
-
453
- with gr.Row():
454
- language_options = gr.Dropdown(
455
- choices=language,
456
- label="Language",
457
- value="中文",
458
- elem_id="language",
459
- )
460
- face_detect_model_options = gr.Dropdown(
461
- choices=face_detect_model_list,
462
- label="人脸检测模型",
463
- value=face_detect_model_list[0],
464
- elem_id="matting_model",
465
- )
466
- matting_model_options = gr.Dropdown(
467
- choices=matting_model_list,
468
- label="抠图模型",
469
- value=matting_model_list[0],
470
- elem_id="matting_model",
471
- )
472
-
473
- with gr.Tab("核心参数") as key_parameter_tab:
474
- mode_options = gr.Radio(
475
- choices=size_mode_CN,
476
- label="证件照尺寸选项",
477
- value="尺寸列表",
478
- elem_id="size",
479
- )
480
-
481
- # 预设尺寸下拉菜单
482
- with gr.Row(visible=True) as size_list_row:
483
- size_list_options = gr.Dropdown(
484
- choices=size_list_CN,
485
- label="预设尺寸",
486
- value=size_list_CN[0],
487
- elem_id="size_list",
488
- )
489
-
490
- with gr.Row(visible=False) as custom_size:
491
- custom_size_height = gr.Number(
492
- value=413, label="height", interactive=True
493
- )
494
- custom_size_wdith = gr.Number(
495
- value=295, label="width", interactive=True
496
- )
497
-
498
- # 左:背景色选项
499
- color_options = gr.Radio(
500
- choices=colors_CN, label="背景色", value="蓝色", elem_id="color"
501
- )
502
-
503
- # 左:如果选择「自定义底色」,显示 RGB 输入框
504
- with gr.Row(visible=False) as custom_color:
505
- custom_color_R = gr.Number(value=0, label="R", interactive=True)
506
- custom_color_G = gr.Number(value=0, label="G", interactive=True)
507
- custom_color_B = gr.Number(value=0, label="B", interactive=True)
508
-
509
- # 左:渲染方式选项
510
- render_options = gr.Radio(
511
- choices=render_CN,
512
- label="渲染方式",
513
- value="纯色",
514
- elem_id="render",
515
- )
516
-
517
- with gr.Tab("高级参数") as advance_parameter_tab:
518
- # 面部占照片总比例
519
- head_measure_ratio_option = gr.Slider(
520
- minimum=0.1,
521
- maximum=0.5,
522
- value=0.2,
523
- step=0.01,
524
- label="面部比例",
525
- interactive=True,
526
- )
527
- # 人像头顶距离照片顶部的比例
528
- top_distance_option = gr.Slider(
529
- minimum=0.02,
530
- maximum=0.5,
531
- value=0.12,
532
- step=0.01,
533
- label="头距顶距离",
534
- interactive=True,
535
- )
536
-
537
- # 输出照片的KB值
538
- image_kb_options = gr.Radio(
539
- choices=image_kb_CN,
540
- label="设置 KB 大小(结果在右边最底的组件下载)",
541
- value="不设置",
542
- elem_id="image_kb",
543
- )
544
- # 自定义 KB 大小,滑动条,最小 10KB,最大 200KB
545
- with gr.Row(visible=False) as custom_image_kb:
546
- custom_image_kb_size = gr.Slider(
547
- minimum=10,
548
- maximum=1000,
549
- value=50,
550
- label="KB 大小",
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
- # 案例图片
641
- example_images = gr.Examples(
642
- inputs=[img_input],
643
- examples=[
644
- [path.as_posix()]
645
- for path in sorted(
646
- pathlib.Path(os.path.join(root_dir, "demo/images")).rglob(
647
- "*.jpg"
648
- )
649
- )
650
- ],
651
- )
652
-
653
- # ---------------- 右半边 UI ----------------
654
- with gr.Column():
655
- notification = gr.Text(label="状态", visible=False)
656
- with gr.Row():
657
- img_output_standard = gr.Image(
658
- label="标准照", height=350, format="jpeg"
659
- )
660
- img_output_standard_hd = gr.Image(
661
- label="高清照", height=350, format="jpeg"
662
- )
663
- img_output_layout = gr.Image(
664
- label="六寸排版照", height=350, format="jpeg"
665
- )
666
- file_download = gr.File(label="下载调整 KB 大小后的照片", visible=False)
667
-
668
- # ---------------- 设置隐藏/显示组件 ----------------
669
- def change_language(language):
670
- # 将Gradio组件中的内容改为中文或英文
671
- if language == "中文":
672
- return {
673
- size_list_options: gr.update(
674
- label="预设尺寸",
675
- choices=size_list_CN,
676
- value=size_list_CN[0],
677
- ),
678
- mode_options: gr.update(
679
- label="证件照尺寸选项",
680
- choices=size_mode_CN,
681
- value="尺寸列表",
682
- ),
683
- color_options: gr.update(
684
- label="背景色",
685
- choices=colors_CN,
686
- value="蓝色",
687
- ),
688
- img_but: gr.update(value="开始制作"),
689
- render_options: gr.update(
690
- label="渲染方式",
691
- choices=render_CN,
692
- value="纯色",
693
- ),
694
- image_kb_options: gr.update(
695
- label="设置 KB 大小(结果在右边最底的组件下载)",
696
- choices=image_kb_CN,
697
- value="不设置",
698
- ),
699
- matting_model_options: gr.update(label="抠图模型"),
700
- face_detect_model_options: gr.update(label="人脸检测模型"),
701
- custom_image_kb_size: gr.update(label="KB 大小"),
702
- notification: gr.update(label="状态"),
703
- img_output_standard: gr.update(label="标准照"),
704
- img_output_standard_hd: gr.update(label="高清照"),
705
- img_output_layout: gr.update(label="六寸排版照"),
706
- file_download: gr.update(label="下载调整 KB 大小后的照片"),
707
- head_measure_ratio_option: gr.update(label="面部比例"),
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":
724
- return {
725
- size_list_options: gr.update(
726
- label="Default size",
727
- choices=size_list_EN,
728
- value=size_list_EN[0],
729
- ),
730
- mode_options: gr.update(
731
- label="ID photo size options",
732
- choices=size_mode_EN,
733
- value="Size List",
734
- ),
735
- color_options: gr.update(
736
- label="Background color",
737
- choices=colors_EN,
738
- value="Blue",
739
- ),
740
- img_but: gr.update(value="Start"),
741
- render_options: gr.update(
742
- label="Rendering mode",
743
- choices=render_EN,
744
- value="Solid Color",
745
- ),
746
- image_kb_options: gr.update(
747
- label="Set KB size (Download in the bottom right)",
748
- choices=image_kb_EN,
749
- value="Not Set",
750
- ),
751
- matting_model_options: gr.update(label="Matting model"),
752
- face_detect_model_options: gr.update(label="Face detect model"),
753
- custom_image_kb_size: gr.update(label="KB size"),
754
- notification: gr.update(label="Status"),
755
- img_output_standard: gr.update(label="Standard photo"),
756
- img_output_standard_hd: gr.update(label="HD photo"),
757
- img_output_layout: gr.update(label="Layout photo"),
758
- file_download: gr.update(
759
- label="Download the photo after adjusting the KB size"
760
- ),
761
- head_measure_ratio_option: gr.update(label="Head ratio"),
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):
778
- if colors == "自定义底色" or colors == "Custom Color":
779
- return {custom_color: gr.update(visible=True)}
780
- else:
781
- return {custom_color: gr.update(visible=False)}
782
-
783
- def change_size_mode(size_option_item):
784
- if (
785
- size_option_item == "自定义尺寸"
786
- or size_option_item == "Custom Size"
787
- ):
788
- return {
789
- custom_size: gr.update(visible=True),
790
- size_list_row: gr.update(visible=False),
791
- }
792
- elif (
793
- size_option_item == "只换底"
794
- or size_option_item == "Only Change Background"
795
- ):
796
- return {
797
- custom_size: gr.update(visible=False),
798
- size_list_row: gr.update(visible=False),
799
- }
800
- else:
801
- return {
802
- custom_size: gr.update(visible=False),
803
- size_list_row: gr.update(visible=True),
804
- }
805
-
806
- def change_image_kb(image_kb_option):
807
- if image_kb_option == "自定义" or image_kb_option == "Custom":
808
- return {custom_image_kb: gr.update(visible=True)}
809
- else:
810
- return {custom_image_kb: gr.update(visible=False)}
811
-
812
- # ---------------- 绑定事件 ----------------
813
- language_options.input(
814
- change_language,
815
- inputs=[language_options],
816
- outputs=[
817
- size_list_options,
818
- mode_options,
819
- color_options,
820
- img_but,
821
- render_options,
822
- image_kb_options,
823
- matting_model_options,
824
- face_detect_model_options,
825
- custom_image_kb_size,
826
- notification,
827
- img_output_standard,
828
- img_output_standard_hd,
829
- img_output_layout,
830
- file_download,
831
- head_measure_ratio_option,
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
-
846
- color_options.input(
847
- change_color, inputs=[color_options], outputs=[custom_color]
848
- )
849
-
850
- mode_options.input(
851
- change_size_mode,
852
- inputs=[mode_options],
853
- outputs=[custom_size, size_list_row],
854
- )
855
-
856
- image_kb_options.input(
857
- change_image_kb, inputs=[image_kb_options], outputs=[custom_image_kb]
858
- )
859
-
860
- img_but.click(
861
- idphoto_inference,
862
- inputs=[
863
- img_input,
864
- mode_options,
865
- size_list_options,
866
- color_options,
867
- render_options,
868
- image_kb_options,
869
- custom_color_R,
870
- custom_color_G,
871
- custom_color_B,
872
- custom_size_height,
873
- custom_size_wdith,
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,
887
- ],
888
- outputs=[
889
- img_output_standard,
890
- img_output_standard_hd,
891
- img_output_layout,
892
- notification,
893
- file_download,
894
- ],
895
- )
896
 
 
 
 
897
  demo.launch(
898
  # server_name=args.host,
899
  # server_port=args.port,
900
- show_api=False,
901
  # favicon_path=os.path.join(root_dir, "assets/hivision_logo.png"),
902
  # root_path=args.root_path,
 
903
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import argparse
2
+ import os
3
+ from demo.processor import IDPhotoProcessor
4
+ from demo.ui import create_ui
5
+ from hivision.creator.choose_handler import HUMAN_MATTING_MODELS
6
 
 
7
  root_dir = os.path.dirname(os.path.abspath(__file__))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
+ HUMAN_MATTING_MODELS_EXIST = [
10
+ os.path.splitext(file)[0]
11
+ for file in os.listdir(os.path.join(root_dir, "hivision/creator/weights"))
12
+ if file.endswith(".onnx") or file.endswith(".mnn")
13
+ ]
14
+ # 在HUMAN_MATTING_MODELS中的模型才会被加载到Gradio中显示
15
+ HUMAN_MATTING_MODELS = [
16
+ model for model in HUMAN_MATTING_MODELS if model in HUMAN_MATTING_MODELS_EXIST
17
+ ]
18
+
19
+ FACE_DETECT_MODELS = ["face++ (联网Online API)", "mtcnn"]
20
+ FACE_DETECT_MODELS_EXPAND = (
21
+ ["retinaface-resnet50"]
22
+ if os.path.exists(
23
+ os.path.join(
24
+ root_dir, "hivision/creator/retinaface/weights/retinaface-resnet50.onnx"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  )
27
+ else []
28
+ )
29
+ FACE_DETECT_MODELS += FACE_DETECT_MODELS_EXPAND
30
 
31
  if __name__ == "__main__":
32
  argparser = argparse.ArgumentParser()
 
42
  default=None,
43
  help="The root path of the server, default is None (='/'), e.g. '/myapp'",
44
  )
 
45
  args = argparser.parse_args()
46
 
47
+ processor = IDPhotoProcessor()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ demo = create_ui(
50
+ processor, root_dir, HUMAN_MATTING_MODELS_EXIST, FACE_DETECT_MODELS
51
+ )
52
  demo.launch(
53
  # server_name=args.host,
54
  # server_port=args.port,
 
55
  # favicon_path=os.path.join(root_dir, "assets/hivision_logo.png"),
56
  # root_path=args.root_path,
57
+ show_api=False,
58
  )
demo/assets/color_list_CN.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Name,Hex
2
+ 蓝色,628bce
3
+ 白色,ffffff
4
+ 红色,d74532
5
+ 黑色,000000
6
+ 深蓝色,4b6190
demo/assets/color_list_EN.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Name,Hex
2
+ Blue,628bce
3
+ White,ffffff
4
+ Red,d74532
5
+ Black,000000
6
+ Dark Blue,4b6190
demo/{size_list_CN.csv → assets/size_list_CN.csv} RENAMED
File without changes
demo/{size_list_EN.csv → assets/size_list_EN.csv} RENAMED
File without changes
demo/config.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from demo.utils import csv_to_size_list, csv_to_color_list
3
+
4
+
5
+ def load_configuration(root_dir):
6
+ size_list_dict_CN = csv_to_size_list(
7
+ os.path.join(root_dir, "assets/size_list_CN.csv")
8
+ )
9
+ size_list_dict_EN = csv_to_size_list(
10
+ os.path.join(root_dir, "assets/size_list_EN.csv")
11
+ )
12
+ color_list_dict_CN = csv_to_color_list(
13
+ os.path.join(root_dir, "assets/color_list_CN.csv")
14
+ )
15
+ color_list_dict_EN = csv_to_color_list(
16
+ os.path.join(root_dir, "assets/color_list_EN.csv")
17
+ )
18
+
19
+ return size_list_dict_CN, size_list_dict_EN, color_list_dict_CN, color_list_dict_EN
demo/locals.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 the LlamaFactory team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from demo.utils import csv_to_size_list
16
+ from demo.config import load_configuration
17
+ import os
18
+
19
+ base_dir = os.path.dirname(os.path.abspath(__file__))
20
+ size_list_dict_CN = csv_to_size_list(os.path.join(base_dir, "assets/size_list_CN.csv"))
21
+ size_list_dict_EN = csv_to_size_list(os.path.join(base_dir, "assets/size_list_EN.csv"))
22
+ (
23
+ size_list_config_CN,
24
+ size_list_config_EN,
25
+ color_list_dict_CN,
26
+ color_list_dict_EN,
27
+ ) = load_configuration(base_dir)
28
+
29
+
30
+ LOCALES = {
31
+ "face_model": {
32
+ "en": {
33
+ "label": "Face detection model",
34
+ },
35
+ "zh": {
36
+ "label": "人脸检测模型",
37
+ },
38
+ },
39
+ "matting_model": {
40
+ "en": {
41
+ "label": "Matting model",
42
+ },
43
+ "zh": {
44
+ "label": "抠图模型",
45
+ },
46
+ },
47
+ "key_param": {
48
+ "en": {
49
+ "label": "Key Parameters",
50
+ },
51
+ "zh": {
52
+ "label": "核心参数",
53
+ },
54
+ },
55
+ "advance_param": {
56
+ "en": {
57
+ "label": "Advance Parameters",
58
+ },
59
+ "zh": {
60
+ "label": "高级参数",
61
+ },
62
+ },
63
+ "size_mode": {
64
+ "en": {
65
+ "label": "ID photo size options",
66
+ "choices": ["Size List", "Only Change Background", "Custom Size"],
67
+ "custom_size_eror": "The width should not be greater than the length; the length and width should not be less than 100, and no more than 1800.",
68
+ },
69
+ "zh": {
70
+ "label": "证件照尺寸选项",
71
+ "choices": ["尺寸列表", "只换底", "自定义尺寸"],
72
+ "custom_size_eror": "宽度不应大于长度;长度和宽度不应小于100,不大于1800。",
73
+ },
74
+ },
75
+ "size_list": {
76
+ "en": {
77
+ "label": "Size list",
78
+ "choices": list(size_list_dict_EN.keys()),
79
+ "develop": size_list_config_EN,
80
+ },
81
+ "zh": {
82
+ "label": "预设尺寸",
83
+ "choices": list(size_list_dict_CN.keys()),
84
+ "develop": size_list_config_CN,
85
+ },
86
+ },
87
+ "bg_color": {
88
+ "en": {
89
+ "label": "Background color",
90
+ "choices": list(color_list_dict_EN.keys()) + ["Custom"],
91
+ "develop": color_list_dict_EN,
92
+ },
93
+ "zh": {
94
+ "label": "背景颜色",
95
+ "choices": list(color_list_dict_CN.keys()) + ["自定义底色"],
96
+ "develop": color_list_dict_CN,
97
+ },
98
+ },
99
+ "button": {
100
+ "en": {
101
+ "label": "Start",
102
+ },
103
+ "zh": {
104
+ "label": "开始制作",
105
+ },
106
+ },
107
+ "head_measure_ratio": {
108
+ "en": {
109
+ "label": "Head ratio",
110
+ },
111
+ "zh": {
112
+ "label": "面部比例",
113
+ },
114
+ },
115
+ "top_distance": {
116
+ "en": {
117
+ "label": "Top distance",
118
+ },
119
+ "zh": {
120
+ "label": "头距顶距离",
121
+ },
122
+ },
123
+ "image_kb": {
124
+ "en": {
125
+ "label": "Set KB size",
126
+ "choices": ["Not Set", "Custom"],
127
+ },
128
+ "zh": {
129
+ "label": "设置 KB 大小",
130
+ "choices": ["不设置", "自定义"],
131
+ },
132
+ },
133
+ "image_kb_size": {
134
+ "en": {
135
+ "label": "KB size",
136
+ },
137
+ "zh": {
138
+ "label": "KB 大小",
139
+ },
140
+ },
141
+ "render_mode": {
142
+ "en": {
143
+ "label": "Render mode",
144
+ "choices": [
145
+ "Solid Color",
146
+ "Up-Down Gradient (White)",
147
+ "Center Gradient (White)",
148
+ ],
149
+ },
150
+ "zh": {
151
+ "label": "渲染方式",
152
+ "choices": ["纯色", "上下渐变(白色)", "中心渐变(白色)"],
153
+ },
154
+ },
155
+ # Tab3 - 水印工作台
156
+ "watermark_tab": {
157
+ "en": {
158
+ "label": "Watermark",
159
+ },
160
+ "zh": {
161
+ "label": "水印",
162
+ },
163
+ },
164
+ "watermark_text": {
165
+ "en": {
166
+ "label": "Text",
167
+ "value": "Hello",
168
+ "placeholder": "up to 20 characters",
169
+ },
170
+ "zh": {
171
+ "label": "水印文字",
172
+ "value": "Hello",
173
+ "placeholder": "最多20个字符",
174
+ },
175
+ },
176
+ "watermark_color": {
177
+ "en": {
178
+ "label": "Color",
179
+ },
180
+ "zh": {
181
+ "label": "水���颜色",
182
+ },
183
+ },
184
+ "watermark_size": {
185
+ "en": {
186
+ "label": "Size",
187
+ },
188
+ "zh": {
189
+ "label": "文字大小",
190
+ },
191
+ },
192
+ "watermark_opacity": {
193
+ "en": {
194
+ "label": "Opacity",
195
+ },
196
+ "zh": {
197
+ "label": "水印透明度",
198
+ },
199
+ },
200
+ "watermark_angle": {
201
+ "en": {
202
+ "label": "Angle",
203
+ },
204
+ "zh": {
205
+ "label": "水印角度",
206
+ },
207
+ },
208
+ "watermark_space": {
209
+ "en": {
210
+ "label": "Space",
211
+ },
212
+ "zh": {
213
+ "label": "水印间距",
214
+ },
215
+ },
216
+ "watermark_switch": {
217
+ "en": {
218
+ "label": "Watermark",
219
+ "value": "Not Add",
220
+ "choices": ["Not Add", "Add"],
221
+ },
222
+ "zh": {
223
+ "label": "水印",
224
+ "value": "不添加",
225
+ "choices": ["不添加", "添加"],
226
+ },
227
+ },
228
+ # 输出结果
229
+ "notification": {
230
+ "en": {
231
+ "label": "notification",
232
+ "face_error": "The number of faces is not equal to 1, please upload an image with a single face. If the actual number of faces is 1, it may be an issue with the accuracy of the detection model. Please switch to a different face detection model on the left or raise a Github Issue to notify the author.",
233
+ },
234
+ "zh": {
235
+ "label": "通知",
236
+ "face_error": "人脸数不等于1,请上传单人照片。如果实际人脸数为1,可能是检测模型的准确度问题,请切换左侧不同的人脸检测模型或提出Github Issue通知作者。",
237
+ },
238
+ },
239
+ "standard_photo": {
240
+ "en": {
241
+ "label": "Standard photo",
242
+ },
243
+ "zh": {
244
+ "label": "标准照",
245
+ },
246
+ },
247
+ "hd_photo": {
248
+ "en": {
249
+ "label": "HD photo",
250
+ },
251
+ "zh": {
252
+ "label": "高清照",
253
+ },
254
+ },
255
+ "layout_photo": {
256
+ "en": {
257
+ "label": "Layout photo",
258
+ },
259
+ "zh": {
260
+ "label": "六寸排版照",
261
+ },
262
+ },
263
+ "download": {
264
+ "en": {
265
+ "label": "Download the photo after adjusting the KB size",
266
+ },
267
+ "zh": {
268
+ "label": "下载调整 KB 大小后的照片",
269
+ },
270
+ },
271
+ }
demo/processor.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from hivision import IDCreator
3
+ from hivision.error import FaceError, APIError
4
+ from hivision.utils import add_background, resize_image_to_kb
5
+ from hivision.creator.layout_calculator import (
6
+ generate_layout_photo,
7
+ generate_layout_image,
8
+ )
9
+ from hivision.creator.choose_handler import choose_handler
10
+ from demo.utils import add_watermark, range_check
11
+ import gradio as gr
12
+ import os
13
+ import time
14
+ from demo.locals import LOCALES
15
+
16
+
17
+ class IDPhotoProcessor:
18
+ def process(
19
+ self,
20
+ input_image,
21
+ mode_option,
22
+ size_list_option,
23
+ color_option,
24
+ render_option,
25
+ image_kb_options,
26
+ custom_color_R,
27
+ custom_color_G,
28
+ custom_color_B,
29
+ custom_size_height,
30
+ custom_size_width,
31
+ custom_image_kb,
32
+ language,
33
+ matting_model_option,
34
+ watermark_option,
35
+ watermark_text,
36
+ watermark_text_color,
37
+ watermark_text_size,
38
+ watermark_text_opacity,
39
+ watermark_text_angle,
40
+ watermark_text_space,
41
+ face_detect_option,
42
+ head_measure_ratio=0.2,
43
+ top_distance_max=0.12,
44
+ top_distance_min=0.10,
45
+ ):
46
+ top_distance_min = top_distance_max - 0.02
47
+
48
+ idphoto_json = {
49
+ "size_mode": mode_option,
50
+ "color_mode": color_option,
51
+ "render_mode": render_option,
52
+ "image_kb_mode": image_kb_options,
53
+ "custom_image_kb": None,
54
+ }
55
+
56
+ # 如果尺寸模式选择的是尺寸列表
57
+ if idphoto_json["size_mode"] == LOCALES["size_mode"][language]["choices"][0]:
58
+ idphoto_json["size"] = LOCALES["size_list"][language]["develop"][
59
+ size_list_option
60
+ ]
61
+ # 如果尺寸模式选择的是自定义尺寸
62
+ elif idphoto_json["size_mode"] == LOCALES["size_mode"][language]["choices"][2]:
63
+ id_height = int(custom_size_height)
64
+ id_width = int(custom_size_width)
65
+ if (
66
+ id_height < id_width
67
+ or min(id_height, id_width) < 100
68
+ or max(id_height, id_width) > 1800
69
+ ):
70
+ return [
71
+ gr.update(value=None), # img_output_standard
72
+ gr.update(value=None), # img_output_standard_hd
73
+ None, # img_output_layout (assuming it should be None or not updated)
74
+ gr.update( # notification
75
+ value=LOCALES["size_mode"][language]["custom_size_eror"],
76
+ visible=True,
77
+ ),
78
+ None, # file_download (assuming it should be None or not updated)
79
+ ]
80
+
81
+ idphoto_json["size"] = (id_height, id_width)
82
+ else:
83
+ idphoto_json["size"] = (None, None)
84
+
85
+ # 如果颜色模式选择的是自定义底色
86
+ if idphoto_json["color_mode"] == LOCALES["bg_color"][language]["choices"][-1]:
87
+ idphoto_json["color_bgr"] = (
88
+ range_check(custom_color_R),
89
+ range_check(custom_color_G),
90
+ range_check(custom_color_B),
91
+ )
92
+ else:
93
+ hex_color = idphoto_json["color_bgr"] = LOCALES["bg_color"][language][
94
+ "develop"
95
+ ][color_option]
96
+ # 转为 RGB
97
+ idphoto_json["color_bgr"] = tuple(
98
+ int(hex_color[i : i + 2], 16) for i in (0, 2, 4)
99
+ )
100
+
101
+ # 如果输出 KB 大小选择的是自定义
102
+ if (
103
+ idphoto_json["image_kb_mode"]
104
+ == LOCALES["image_kb"][language]["choices"][-1]
105
+ ):
106
+ idphoto_json["custom_image_kb"] = custom_image_kb
107
+
108
+ creator = IDCreator()
109
+ choose_handler(creator, matting_model_option, face_detect_option)
110
+
111
+ # 是否只换底
112
+ change_bg_only = (
113
+ idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]
114
+ )
115
+
116
+ try:
117
+ result = creator(
118
+ input_image,
119
+ change_bg_only=change_bg_only,
120
+ size=idphoto_json["size"],
121
+ head_measure_ratio=head_measure_ratio,
122
+ head_top_range=(top_distance_max, top_distance_min),
123
+ )
124
+ except FaceError:
125
+ return [
126
+ gr.update(value=None), # img_output_standard
127
+ gr.update(value=None), # img_output_standard_hd
128
+ gr.update(visible=False), # img_output_layout
129
+ gr.update( # notification
130
+ value=LOCALES["notification"][language]["face_error"],
131
+ visible=True,
132
+ ),
133
+ None, # file_download (assuming it should be None or have no update)
134
+ ]
135
+
136
+ except APIError as e:
137
+ return [
138
+ gr.update(value=None), # img_output_standard
139
+ gr.update(value=None), # img_output_standard_hd
140
+ gr.update(visible=False), # img_output_layout
141
+ gr.update( # notification
142
+ value=LOCALES["notification"][language]["face_error"],
143
+ visible=True,
144
+ ),
145
+ None, # file_download (assuming it should be None or have no update)
146
+ ]
147
+
148
+ else:
149
+ (result_image_standard, result_image_hd, _, _) = result
150
+ if (
151
+ idphoto_json["render_mode"]
152
+ == LOCALES["render_mode"][language]["choices"][0]
153
+ ):
154
+ result_image_standard = np.uint8(
155
+ add_background(result_image_standard, bgr=idphoto_json["color_bgr"])
156
+ )
157
+ result_image_hd = np.uint8(
158
+ add_background(result_image_hd, bgr=idphoto_json["color_bgr"])
159
+ )
160
+ elif (
161
+ idphoto_json["render_mode"]
162
+ == LOCALES["render_mode"][language]["choices"][1]
163
+ ):
164
+ result_image_standard = np.uint8(
165
+ add_background(
166
+ result_image_standard,
167
+ bgr=idphoto_json["color_bgr"],
168
+ mode="updown_gradient",
169
+ )
170
+ )
171
+ result_image_hd = np.uint8(
172
+ add_background(
173
+ result_image_hd,
174
+ bgr=idphoto_json["color_bgr"],
175
+ mode="updown_gradient",
176
+ )
177
+ )
178
+ else:
179
+ result_image_standard = np.uint8(
180
+ add_background(
181
+ result_image_standard,
182
+ bgr=idphoto_json["color_bgr"],
183
+ mode="center_gradient",
184
+ )
185
+ )
186
+ result_image_hd = np.uint8(
187
+ add_background(
188
+ result_image_hd,
189
+ bgr=idphoto_json["color_bgr"],
190
+ mode="center_gradient",
191
+ )
192
+ )
193
+
194
+ # 如果只换底,就不生成排版照
195
+ if change_bg_only:
196
+ result_layout_image = gr.update(visible=False)
197
+ else:
198
+ typography_arr, typography_rotate = generate_layout_photo(
199
+ input_height=idphoto_json["size"][0],
200
+ input_width=idphoto_json["size"][1],
201
+ )
202
+
203
+ if (
204
+ watermark_option
205
+ == LOCALES["watermark_switch"][language]["choices"][1]
206
+ ):
207
+ result_layout_image = gr.update(
208
+ value=generate_layout_image(
209
+ add_watermark(
210
+ image=result_image_standard,
211
+ text=watermark_text,
212
+ size=watermark_text_size,
213
+ opacity=watermark_text_opacity,
214
+ angle=watermark_text_angle,
215
+ space=watermark_text_space,
216
+ color=watermark_text_color,
217
+ ),
218
+ typography_arr,
219
+ typography_rotate,
220
+ height=idphoto_json["size"][0],
221
+ width=idphoto_json["size"][1],
222
+ ),
223
+ visible=True,
224
+ )
225
+ else:
226
+ result_layout_image = gr.update(
227
+ value=generate_layout_image(
228
+ result_image_standard,
229
+ typography_arr,
230
+ typography_rotate,
231
+ height=idphoto_json["size"][0],
232
+ width=idphoto_json["size"][1],
233
+ ),
234
+ visible=True,
235
+ )
236
+
237
+ # 如果添加水印
238
+ if watermark_option == LOCALES["watermark_switch"][language]["choices"][1]:
239
+ result_image_standard = add_watermark(
240
+ image=result_image_standard,
241
+ text=watermark_text,
242
+ size=watermark_text_size,
243
+ opacity=watermark_text_opacity,
244
+ angle=watermark_text_angle,
245
+ space=watermark_text_space,
246
+ color=watermark_text_color,
247
+ )
248
+ result_image_hd = add_watermark(
249
+ image=result_image_hd,
250
+ text=watermark_text,
251
+ size=watermark_text_size,
252
+ opacity=watermark_text_opacity,
253
+ angle=watermark_text_angle,
254
+ space=watermark_text_space,
255
+ color=watermark_text_color,
256
+ )
257
+
258
+ # 如果输出 KB 大小选择的是自定义
259
+ if idphoto_json["custom_image_kb"]:
260
+ print("调整 kb 大小到", idphoto_json["custom_image_kb"], "kb")
261
+ output_image_path = f"{os.path.join(os.path.dirname(os.path.dirname(__file__)), 'demo/kb_output')}/{int(time.time())}.jpg"
262
+ resize_image_to_kb(
263
+ result_image_standard,
264
+ output_image_path,
265
+ idphoto_json["custom_image_kb"],
266
+ )
267
+ else:
268
+ output_image_path = None
269
+
270
+ # 返回结果
271
+ if output_image_path:
272
+ return [
273
+ result_image_standard, # img_output_standard
274
+ result_image_hd, # img_output_standard_hd
275
+ result_layout_image, # img_output_layout
276
+ gr.update(visible=False), # notification
277
+ gr.update(visible=True, value=output_image_path), # file_download
278
+ ]
279
+ else:
280
+ return [
281
+ result_image_standard, # img_output_standard
282
+ result_image_hd, # img_output_standard_hd
283
+ result_layout_image, # img_output_layout
284
+ gr.update(visible=False), # notification
285
+ gr.update(visible=False), # file_download
286
+ ]
demo/ui.py ADDED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import pathlib
4
+ from demo.locals import LOCALES
5
+ from hivision.creator.choose_handler import FACE_DETECT_MODELS
6
+
7
+
8
+ def load_description(fp):
9
+ with open(fp, "r", encoding="utf-8") as f:
10
+ content = f.read()
11
+ return content
12
+
13
+
14
+ def create_ui(
15
+ processor, root_dir, human_matting_models: list, face_detect_models: list
16
+ ):
17
+ DEFAULT_LANG = "zh"
18
+ DEFAULT_HUMAN_MATTING_MODEL = "modnet_photographic_portrait_matting"
19
+ DEFAULT_FACE_DETECT_MODEL = "mtcnn"
20
+
21
+ if DEFAULT_HUMAN_MATTING_MODEL in human_matting_models:
22
+ human_matting_models.remove(DEFAULT_HUMAN_MATTING_MODEL)
23
+ human_matting_models.insert(0, DEFAULT_HUMAN_MATTING_MODEL)
24
+
25
+ css = """
26
+ #col-left {
27
+ margin: 0 auto;
28
+ max-width: 430px;
29
+ }
30
+ #col-mid {
31
+ margin: 0 auto;
32
+ max-width: 430px;
33
+ }
34
+ #col-right {
35
+ margin: 0 auto;
36
+ max-width: 430px;
37
+ }
38
+ #col-showcase {
39
+ margin: 0 auto;
40
+ max-width: 1100px;
41
+ }
42
+ #button {
43
+ color: blue;
44
+ }
45
+ """
46
+
47
+ demo = gr.Blocks(title="HivisionIDPhotos", css=css)
48
+
49
+ with demo:
50
+ gr.HTML(load_description(os.path.join(root_dir, "assets/title.md")))
51
+ with gr.Row():
52
+ # ------------ 左半边 UI ----------------
53
+ with gr.Column():
54
+ img_input = gr.Image(height=400)
55
+
56
+ with gr.Row():
57
+ # 语言选择器
58
+ language = ["zh", "en"]
59
+ language_options = gr.Dropdown(
60
+ choices=language,
61
+ label="Language",
62
+ value=DEFAULT_LANG,
63
+ )
64
+
65
+ face_detect_model_options = gr.Dropdown(
66
+ choices=face_detect_models,
67
+ label=LOCALES["face_model"][DEFAULT_LANG]["label"],
68
+ value=DEFAULT_FACE_DETECT_MODEL,
69
+ )
70
+
71
+ matting_model_options = gr.Dropdown(
72
+ choices=human_matting_models,
73
+ label=LOCALES["matting_model"][DEFAULT_LANG]["label"],
74
+ value=human_matting_models[0],
75
+ )
76
+
77
+ with gr.Tab(
78
+ LOCALES["key_param"][DEFAULT_LANG]["label"]
79
+ ) as key_parameter_tab:
80
+ mode_options = gr.Radio(
81
+ choices=LOCALES["size_mode"][DEFAULT_LANG]["choices"],
82
+ label=LOCALES["size_mode"][DEFAULT_LANG]["label"],
83
+ value=LOCALES["size_mode"][DEFAULT_LANG]["choices"][0],
84
+ )
85
+
86
+ with gr.Row(visible=True) as size_list_row:
87
+ size_list_options = gr.Dropdown(
88
+ choices=LOCALES["size_list"][DEFAULT_LANG]["choices"],
89
+ label="预设尺寸",
90
+ value=LOCALES["size_list"][DEFAULT_LANG]["choices"][0],
91
+ elem_id="size_list",
92
+ )
93
+
94
+ with gr.Row(visible=False) as custom_size:
95
+ custom_size_height = gr.Number(
96
+ value=413, label="height", interactive=True
97
+ )
98
+ custom_size_width = gr.Number(
99
+ value=295, label="width", interactive=True
100
+ )
101
+
102
+ color_options = gr.Radio(
103
+ choices=LOCALES["bg_color"][DEFAULT_LANG]["choices"],
104
+ label=LOCALES["bg_color"][DEFAULT_LANG]["label"],
105
+ value=LOCALES["bg_color"][DEFAULT_LANG]["choices"][0],
106
+ )
107
+
108
+ with gr.Row(visible=False) as custom_color:
109
+ custom_color_R = gr.Number(value=0, label="R", interactive=True)
110
+ custom_color_G = gr.Number(value=0, label="G", interactive=True)
111
+ custom_color_B = gr.Number(value=0, label="B", interactive=True)
112
+
113
+ render_options = gr.Radio(
114
+ choices=LOCALES["render_mode"][DEFAULT_LANG]["choices"],
115
+ label=LOCALES["render_mode"][DEFAULT_LANG]["label"],
116
+ value=LOCALES["render_mode"][DEFAULT_LANG]["choices"][0],
117
+ )
118
+
119
+ with gr.Tab(
120
+ LOCALES["advance_param"][DEFAULT_LANG]["label"]
121
+ ) as advance_parameter_tab:
122
+ head_measure_ratio_option = gr.Slider(
123
+ minimum=0.1,
124
+ maximum=0.5,
125
+ value=0.2,
126
+ step=0.01,
127
+ label=LOCALES["head_measure_ratio"][DEFAULT_LANG]["label"],
128
+ interactive=True,
129
+ )
130
+ top_distance_option = gr.Slider(
131
+ minimum=0.02,
132
+ maximum=0.5,
133
+ value=0.12,
134
+ step=0.01,
135
+ label=LOCALES["top_distance"][DEFAULT_LANG]["label"],
136
+ interactive=True,
137
+ )
138
+
139
+ image_kb_options = gr.Radio(
140
+ choices=LOCALES["image_kb"][DEFAULT_LANG]["choices"],
141
+ label=LOCALES["image_kb"][DEFAULT_LANG]["label"],
142
+ value=LOCALES["image_kb"][DEFAULT_LANG]["choices"][0],
143
+ )
144
+
145
+ with gr.Row(visible=False) as custom_image_kb:
146
+ custom_image_kb_size = gr.Slider(
147
+ minimum=10,
148
+ maximum=1000,
149
+ value=50,
150
+ label=LOCALES["image_kb_size"][DEFAULT_LANG]["label"],
151
+ interactive=True,
152
+ )
153
+
154
+ with gr.Tab(
155
+ LOCALES["watermark_tab"][DEFAULT_LANG]["label"]
156
+ ) as watermark_parameter_tab:
157
+ watermark_options = gr.Radio(
158
+ choices=LOCALES["watermark_switch"][DEFAULT_LANG]["choices"],
159
+ label=LOCALES["watermark_switch"][DEFAULT_LANG]["label"],
160
+ value=LOCALES["watermark_switch"][DEFAULT_LANG]["choices"][0],
161
+ )
162
+
163
+ with gr.Row():
164
+ watermark_text_options = gr.Text(
165
+ max_length=20,
166
+ label=LOCALES["watermark_text"][DEFAULT_LANG]["label"],
167
+ value=LOCALES["watermark_text"][DEFAULT_LANG]["value"],
168
+ placeholder=LOCALES["watermark_text"][DEFAULT_LANG][
169
+ "placeholder"
170
+ ],
171
+ interactive=False,
172
+ )
173
+ watermark_text_color = gr.ColorPicker(
174
+ label=LOCALES["watermark_color"][DEFAULT_LANG]["label"],
175
+ interactive=False,
176
+ value="#FFFFFF",
177
+ )
178
+
179
+ watermark_text_size = gr.Slider(
180
+ minimum=10,
181
+ maximum=100,
182
+ value=20,
183
+ label=LOCALES["watermark_size"][DEFAULT_LANG]["label"],
184
+ interactive=False,
185
+ step=1,
186
+ )
187
+
188
+ watermark_text_opacity = gr.Slider(
189
+ minimum=0,
190
+ maximum=1,
191
+ value=0.15,
192
+ label=LOCALES["watermark_opacity"][DEFAULT_LANG]["label"],
193
+ interactive=False,
194
+ step=0.01,
195
+ )
196
+
197
+ watermark_text_angle = gr.Slider(
198
+ minimum=0,
199
+ maximum=360,
200
+ value=30,
201
+ label=LOCALES["watermark_angle"][DEFAULT_LANG]["label"],
202
+ interactive=False,
203
+ step=1,
204
+ )
205
+
206
+ watermark_text_space = gr.Slider(
207
+ minimum=10,
208
+ maximum=200,
209
+ value=25,
210
+ label=LOCALES["watermark_space"][DEFAULT_LANG]["label"],
211
+ interactive=False,
212
+ step=1,
213
+ )
214
+
215
+ def update_watermark_text_visibility(choice, language):
216
+ return [
217
+ gr.update(
218
+ interactive=(
219
+ choice
220
+ == LOCALES["watermark_switch"][language]["choices"][
221
+ 1
222
+ ]
223
+ )
224
+ )
225
+ ] * 6
226
+
227
+ watermark_options.change(
228
+ fn=update_watermark_text_visibility,
229
+ inputs=[watermark_options, language_options],
230
+ outputs=[
231
+ watermark_text_options,
232
+ watermark_text_color,
233
+ watermark_text_size,
234
+ watermark_text_opacity,
235
+ watermark_text_angle,
236
+ watermark_text_space,
237
+ ],
238
+ )
239
+
240
+ img_but = gr.Button(LOCALES["button"][DEFAULT_LANG]["label"])
241
+
242
+ example_images = gr.Examples(
243
+ inputs=[img_input],
244
+ examples=[
245
+ [path.as_posix()]
246
+ for path in sorted(
247
+ pathlib.Path(os.path.join(root_dir, "demo/images")).rglob(
248
+ "*.jpg"
249
+ )
250
+ )
251
+ ],
252
+ )
253
+
254
+ # ---------------- 右半边 UI ----------------
255
+ with gr.Column():
256
+ notification = gr.Text(
257
+ label=LOCALES["notification"][DEFAULT_LANG]["label"], visible=False
258
+ )
259
+ with gr.Row():
260
+ img_output_standard = gr.Image(
261
+ label=LOCALES["standard_photo"][DEFAULT_LANG]["label"],
262
+ height=350,
263
+ format="jpeg",
264
+ )
265
+ img_output_standard_hd = gr.Image(
266
+ label=LOCALES["hd_photo"][DEFAULT_LANG]["label"],
267
+ height=350,
268
+ format="jpeg",
269
+ )
270
+ img_output_layout = gr.Image(
271
+ label=LOCALES["layout_photo"][DEFAULT_LANG]["label"],
272
+ height=350,
273
+ format="jpeg",
274
+ )
275
+ file_download = gr.File(
276
+ label=LOCALES["download"][DEFAULT_LANG]["label"], visible=False
277
+ )
278
+
279
+ # ---------------- 设置隐藏/显示组件 ----------------
280
+ def change_language(language):
281
+ return {
282
+ face_detect_model_options: gr.update(
283
+ label=LOCALES["face_model"][language]["label"]
284
+ ),
285
+ matting_model_options: gr.update(
286
+ label=LOCALES["matting_model"][language]["label"]
287
+ ),
288
+ size_list_options: gr.update(
289
+ label=LOCALES["size_list"][language]["label"],
290
+ choices=LOCALES["size_list"][language]["choices"],
291
+ value=LOCALES["size_list"][language]["choices"][0],
292
+ ),
293
+ mode_options: gr.update(
294
+ label=LOCALES["size_mode"][language]["label"],
295
+ choices=LOCALES["size_mode"][language]["choices"],
296
+ value=LOCALES["size_mode"][language]["choices"][0],
297
+ ),
298
+ color_options: gr.update(
299
+ label=LOCALES["bg_color"][language]["label"],
300
+ choices=LOCALES["bg_color"][language]["choices"],
301
+ value=LOCALES["bg_color"][language]["choices"][0],
302
+ ),
303
+ img_but: gr.update(value=LOCALES["button"][language]["label"]),
304
+ render_options: gr.update(
305
+ label=LOCALES["render_mode"][language]["label"],
306
+ choices=LOCALES["render_mode"][language]["choices"],
307
+ value=LOCALES["render_mode"][language]["choices"][0],
308
+ ),
309
+ image_kb_options: gr.update(
310
+ label=LOCALES["image_kb_size"][language]["label"],
311
+ choices=LOCALES["image_kb"][language]["choices"],
312
+ value=LOCALES["image_kb"][language]["choices"][0],
313
+ ),
314
+ custom_image_kb_size: gr.update(
315
+ label=LOCALES["image_kb"][language]["label"]
316
+ ),
317
+ notification: gr.update(
318
+ label=LOCALES["notification"][language]["label"]
319
+ ),
320
+ img_output_standard: gr.update(
321
+ label=LOCALES["standard_photo"][language]["label"]
322
+ ),
323
+ img_output_standard_hd: gr.update(
324
+ label=LOCALES["hd_photo"][language]["label"]
325
+ ),
326
+ img_output_layout: gr.update(
327
+ label=LOCALES["layout_photo"][language]["label"]
328
+ ),
329
+ file_download: gr.update(
330
+ label=LOCALES["download"][language]["label"]
331
+ ),
332
+ head_measure_ratio_option: gr.update(
333
+ label=LOCALES["head_measure_ratio"][language]["label"]
334
+ ),
335
+ top_distance_option: gr.update(
336
+ label=LOCALES["top_distance"][language]["label"]
337
+ ),
338
+ key_parameter_tab: gr.update(
339
+ label=LOCALES["key_param"][language]["label"]
340
+ ),
341
+ advance_parameter_tab: gr.update(
342
+ label=LOCALES["advance_param"][language]["label"]
343
+ ),
344
+ watermark_parameter_tab: gr.update(
345
+ label=LOCALES["watermark_tab"][language]["label"]
346
+ ),
347
+ watermark_text_options: gr.update(
348
+ label=LOCALES["watermark_text"][language]["label"],
349
+ placeholder=LOCALES["watermark_text"][language]["placeholder"],
350
+ ),
351
+ watermark_text_color: gr.update(
352
+ label=LOCALES["watermark_color"][language]["label"]
353
+ ),
354
+ watermark_text_size: gr.update(
355
+ label=LOCALES["watermark_size"][language]["label"]
356
+ ),
357
+ watermark_text_opacity: gr.update(
358
+ label=LOCALES["watermark_opacity"][language]["label"]
359
+ ),
360
+ watermark_text_angle: gr.update(
361
+ label=LOCALES["watermark_angle"][language]["label"]
362
+ ),
363
+ watermark_text_space: gr.update(
364
+ label=LOCALES["watermark_space"][language]["label"]
365
+ ),
366
+ watermark_options: gr.update(
367
+ label=LOCALES["watermark_switch"][language]["label"],
368
+ choices=LOCALES["watermark_switch"][language]["choices"],
369
+ value=LOCALES["watermark_switch"][language]["choices"][0],
370
+ ),
371
+ }
372
+
373
+ def change_color(colors):
374
+ if colors == "自定义底色" or colors == "Custom Color":
375
+ return {custom_color: gr.update(visible=True)}
376
+ else:
377
+ return {custom_color: gr.update(visible=False)}
378
+
379
+ def change_size_mode(size_option_item):
380
+ if (
381
+ size_option_item == "自定义尺寸"
382
+ or size_option_item == "Custom Size"
383
+ ):
384
+ return {
385
+ custom_size: gr.update(visible=True),
386
+ size_list_row: gr.update(visible=False),
387
+ }
388
+ elif (
389
+ size_option_item == "只换底"
390
+ or size_option_item == "Only Change Background"
391
+ ):
392
+ return {
393
+ custom_size: gr.update(visible=False),
394
+ size_list_row: gr.update(visible=False),
395
+ }
396
+ else:
397
+ return {
398
+ custom_size: gr.update(visible=False),
399
+ size_list_row: gr.update(visible=True),
400
+ }
401
+
402
+ def change_image_kb(image_kb_option):
403
+ if image_kb_option == "自定义" or image_kb_option == "Custom":
404
+ return {custom_image_kb: gr.update(visible=True)}
405
+ else:
406
+ return {custom_image_kb: gr.update(visible=False)}
407
+
408
+ # ---------------- 绑定事件 ----------------
409
+ language_options.input(
410
+ change_language,
411
+ inputs=[language_options],
412
+ outputs=[
413
+ size_list_options,
414
+ mode_options,
415
+ color_options,
416
+ img_but,
417
+ render_options,
418
+ image_kb_options,
419
+ matting_model_options,
420
+ face_detect_model_options,
421
+ custom_image_kb_size,
422
+ notification,
423
+ img_output_standard,
424
+ img_output_standard_hd,
425
+ img_output_layout,
426
+ file_download,
427
+ head_measure_ratio_option,
428
+ top_distance_option,
429
+ key_parameter_tab,
430
+ advance_parameter_tab,
431
+ watermark_parameter_tab,
432
+ watermark_text_options,
433
+ watermark_text_color,
434
+ watermark_text_size,
435
+ watermark_text_opacity,
436
+ watermark_text_angle,
437
+ watermark_text_space,
438
+ watermark_options,
439
+ ],
440
+ )
441
+
442
+ color_options.input(
443
+ change_color, inputs=[color_options], outputs=[custom_color]
444
+ )
445
+
446
+ mode_options.input(
447
+ change_size_mode,
448
+ inputs=[mode_options],
449
+ outputs=[custom_size, size_list_row],
450
+ )
451
+
452
+ image_kb_options.input(
453
+ change_image_kb, inputs=[image_kb_options], outputs=[custom_image_kb]
454
+ )
455
+
456
+ img_but.click(
457
+ processor.process,
458
+ inputs=[
459
+ img_input,
460
+ mode_options,
461
+ size_list_options,
462
+ color_options,
463
+ render_options,
464
+ image_kb_options,
465
+ custom_color_R,
466
+ custom_color_G,
467
+ custom_color_B,
468
+ custom_size_height,
469
+ custom_size_width,
470
+ custom_image_kb_size,
471
+ language_options,
472
+ matting_model_options,
473
+ watermark_options,
474
+ watermark_text_options,
475
+ watermark_text_color,
476
+ watermark_text_size,
477
+ watermark_text_opacity,
478
+ watermark_text_angle,
479
+ watermark_text_space,
480
+ face_detect_model_options,
481
+ head_measure_ratio_option,
482
+ top_distance_option,
483
+ ],
484
+ outputs=[
485
+ img_output_standard,
486
+ img_output_standard_hd,
487
+ img_output_layout,
488
+ notification,
489
+ file_download,
490
+ ],
491
+ )
492
+
493
+ return demo
demo/utils.py CHANGED
@@ -1,4 +1,7 @@
1
  import csv
 
 
 
2
 
3
 
4
  def csv_to_size_list(csv_file: str) -> dict:
@@ -17,3 +20,42 @@ def csv_to_size_list(csv_file: str) -> dict:
17
  size_list_dict[size_name_add_size] = (int(h), int(w))
18
 
19
  return size_list_dict
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import csv
2
+ import numpy as np
3
+ from PIL import Image
4
+ from hivision.plugin.watermark import Watermarker, WatermarkerStyles
5
 
6
 
7
  def csv_to_size_list(csv_file: str) -> dict:
 
20
  size_list_dict[size_name_add_size] = (int(h), int(w))
21
 
22
  return size_list_dict
23
+
24
+
25
+ def csv_to_color_list(csv_file: str) -> dict:
26
+ # 初始化一个空字典
27
+ color_list_dict = {}
28
+
29
+ # 打开 CSV 文件并读取数据
30
+ with open(csv_file, mode="r", encoding="utf-8") as file:
31
+ reader = csv.reader(file)
32
+ # 跳过表头
33
+ next(reader)
34
+ # 读取数据并填充字典
35
+ for row in reader:
36
+ color_name, hex_code = row
37
+ color_list_dict[color_name] = hex_code
38
+
39
+ return color_list_dict
40
+
41
+
42
+ def range_check(value, min_value=0, max_value=255):
43
+ value = int(value)
44
+ return max(min_value, min(value, max_value))
45
+
46
+
47
+ def add_watermark(
48
+ image, text, size=50, opacity=0.5, angle=45, color="#8B8B1B", space=75
49
+ ):
50
+ image = Image.fromarray(image)
51
+ watermarker = Watermarker(
52
+ input_image=image,
53
+ text=text,
54
+ style=WatermarkerStyles.STRIPED,
55
+ angle=angle,
56
+ color=color,
57
+ opacity=opacity,
58
+ size=size,
59
+ space=space,
60
+ )
61
+ return np.array(watermarker.image.convert("RGB"))
hivision/creator/choose_handler.py CHANGED
@@ -2,6 +2,16 @@ from hivision.creator.human_matting import *
2
  from hivision.creator.face_detector import *
3
 
4
 
 
 
 
 
 
 
 
 
 
 
5
  def choose_handler(creator, matting_model_option=None, face_detect_option=None):
6
  if matting_model_option == "modnet_photographic_portrait_matting":
7
  creator.matting_handler = extract_human_modnet_photographic_portrait_matting
@@ -9,8 +19,6 @@ def choose_handler(creator, matting_model_option=None, face_detect_option=None):
9
  creator.matting_handler = extract_human_mnn_modnet
10
  elif matting_model_option == "rmbg-1.4":
11
  creator.matting_handler = extract_human_rmbg
12
- # elif matting_model_option == "birefnet-portrait":
13
- # creator.matting_handler = extract_human_birefnet_portrait
14
  elif matting_model_option == "birefnet-v1-lite":
15
  creator.matting_handler = extract_human_birefnet_lite
16
  else:
@@ -18,8 +26,10 @@ def choose_handler(creator, matting_model_option=None, face_detect_option=None):
18
 
19
  if (
20
  face_detect_option == "face_plusplus"
21
- or face_detect_option == "face++ (联网API)"
22
  ):
23
  creator.detection_handler = detect_face_face_plusplus
 
 
24
  else:
25
  creator.detection_handler = detect_face_mtcnn
 
2
  from hivision.creator.face_detector import *
3
 
4
 
5
+ HUMAN_MATTING_MODELS = [
6
+ "modnet_photographic_portrait_matting",
7
+ "birefnet-v1-lite",
8
+ "hivision_modnet",
9
+ "rmbg-1.4",
10
+ ]
11
+
12
+ FACE_DETECT_MODELS = ["face++ (联网Online API)", "mtcnn", "retinaface-resnet50"]
13
+
14
+
15
  def choose_handler(creator, matting_model_option=None, face_detect_option=None):
16
  if matting_model_option == "modnet_photographic_portrait_matting":
17
  creator.matting_handler = extract_human_modnet_photographic_portrait_matting
 
19
  creator.matting_handler = extract_human_mnn_modnet
20
  elif matting_model_option == "rmbg-1.4":
21
  creator.matting_handler = extract_human_rmbg
 
 
22
  elif matting_model_option == "birefnet-v1-lite":
23
  creator.matting_handler = extract_human_birefnet_lite
24
  else:
 
26
 
27
  if (
28
  face_detect_option == "face_plusplus"
29
+ or face_detect_option == "face++ (联网Online API)"
30
  ):
31
  creator.detection_handler = detect_face_face_plusplus
32
+ elif face_detect_option == "retinaface-resnet50":
33
+ creator.detection_handler = detect_face_retinaface
34
  else:
35
  creator.detection_handler = detect_face_mtcnn
hivision/creator/face_detector.py CHANGED
@@ -7,16 +7,24 @@ r"""
7
  @Description:
8
  人脸检测器
9
  """
10
- from mtcnnruntime import MTCNN
 
 
 
 
 
11
  from .context import Context
12
  from hivision.error import FaceError, APIError
13
  from hivision.utils import resize_image_to_kb_base64
 
14
  import requests
15
  import cv2
16
  import os
17
 
18
 
19
  mtcnn = None
 
 
20
 
21
 
22
  def detect_face_mtcnn(ctx: Context, scale: int = 2):
@@ -124,3 +132,45 @@ def detect_face_face_plusplus(ctx: Context):
124
  f"Face++ Status code {status_code} Request entity too large: The image exceeds the 2MB limit.",
125
  status_code,
126
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  @Description:
8
  人脸检测器
9
  """
10
+ try:
11
+ from mtcnnruntime import MTCNN
12
+ except ImportError:
13
+ raise ImportError(
14
+ "Please install mtcnn-runtime by running `pip install mtcnn-runtime`"
15
+ )
16
  from .context import Context
17
  from hivision.error import FaceError, APIError
18
  from hivision.utils import resize_image_to_kb_base64
19
+ from hivision.creator.retinaface import retinaface_detect_faces
20
  import requests
21
  import cv2
22
  import os
23
 
24
 
25
  mtcnn = None
26
+ base_dir = os.path.dirname(os.path.abspath(__file__))
27
+ RETINAFCE_SESS = None
28
 
29
 
30
  def detect_face_mtcnn(ctx: Context, scale: int = 2):
 
132
  f"Face++ Status code {status_code} Request entity too large: The image exceeds the 2MB limit.",
133
  status_code,
134
  )
135
+
136
+
137
+ def detect_face_retinaface(ctx: Context):
138
+ """
139
+ 基于RetinaFace模型的人脸检测处理器,只进行人脸数量的检测
140
+ :param ctx: 上下文,此时已获取到原始图和抠图结果,但是我们只需要原始图
141
+ :raise FaceError: 人脸检测错误,多个人脸或者没有人脸
142
+ """
143
+ from time import time
144
+
145
+ global RETINAFCE_SESS
146
+
147
+ if RETINAFCE_SESS is None:
148
+ print("首次加载RetinaFace模型...")
149
+ # 计算用时
150
+ tic = time()
151
+ faces_dets, sess = retinaface_detect_faces(
152
+ ctx.origin_image,
153
+ os.path.join(base_dir, "retinaface/weights/retinaface-resnet50.onnx"),
154
+ sess=None,
155
+ )
156
+ RETINAFCE_SESS = sess
157
+ print("首次RetinaFace模型推理用时: {:.4f}s".format(time() - tic))
158
+ else:
159
+ tic = time()
160
+ faces_dets, _ = retinaface_detect_faces(
161
+ ctx.origin_image,
162
+ os.path.join(base_dir, "retinaface/weights/retinaface-resnet50.onnx"),
163
+ sess=RETINAFCE_SESS,
164
+ )
165
+ print("二次RetinaFace模型推理用时: {:.4f}s".format(time() - tic))
166
+
167
+ faces_num = len(faces_dets)
168
+ if faces_num != 1:
169
+ raise FaceError("Expected 1 face, but got {}".format(faces_num), faces_num)
170
+ face_det = faces_dets[0]
171
+ ctx.face = (
172
+ face_det[0],
173
+ face_det[1],
174
+ face_det[2] - face_det[0] + 1,
175
+ face_det[3] - face_det[1] + 1,
176
+ )
hivision/creator/retinaface/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .inference import retinaface_detect_faces
hivision/creator/retinaface/box_utils.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+
4
+ def decode(loc, priors, variances):
5
+ """Decode locations from predictions using priors to undo
6
+ the encoding we did for offset regression at train time.
7
+ Args:
8
+ loc (tensor): location predictions for loc layers,
9
+ Shape: [num_priors,4]
10
+ priors (tensor): Prior boxes in center-offset form.
11
+ Shape: [num_priors,4].
12
+ variances: (list[float]) Variances of priorboxes
13
+ Return:
14
+ decoded bounding box predictions
15
+ """
16
+
17
+ boxes = None
18
+
19
+ boxes = np.concatenate(
20
+ (
21
+ priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:],
22
+ priors[:, 2:] * np.exp(loc[:, 2:] * variances[1]),
23
+ ),
24
+ axis=1,
25
+ )
26
+
27
+ boxes[:, :2] -= boxes[:, 2:] / 2
28
+ boxes[:, 2:] += boxes[:, :2]
29
+ return boxes
30
+
31
+
32
+ def decode_landm(pre, priors, variances):
33
+ """Decode landm from predictions using priors to undo
34
+ the encoding we did for offset regression at train time.
35
+ Args:
36
+ pre (tensor): landm predictions for loc layers,
37
+ Shape: [num_priors,10]
38
+ priors (tensor): Prior boxes in center-offset form.
39
+ Shape: [num_priors,4].
40
+ variances: (list[float]) Variances of priorboxes
41
+ Return:
42
+ decoded landm predictions
43
+ """
44
+ landms = None
45
+
46
+ landms = np.concatenate(
47
+ (
48
+ priors[:, :2] + pre[:, :2] * variances[0] * priors[:, 2:],
49
+ priors[:, :2] + pre[:, 2:4] * variances[0] * priors[:, 2:],
50
+ priors[:, :2] + pre[:, 4:6] * variances[0] * priors[:, 2:],
51
+ priors[:, :2] + pre[:, 6:8] * variances[0] * priors[:, 2:],
52
+ priors[:, :2] + pre[:, 8:10] * variances[0] * priors[:, 2:],
53
+ ),
54
+ axis=1,
55
+ )
56
+
57
+ return landms
hivision/creator/retinaface/inference.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+ import onnxruntime as ort
4
+ from hivision.creator.retinaface.box_utils import decode, decode_landm
5
+ from hivision.creator.retinaface.prior_box import PriorBox
6
+ import argparse
7
+
8
+
9
+ def py_cpu_nms(dets, thresh):
10
+ """Pure Python NMS baseline."""
11
+ x1 = dets[:, 0]
12
+ y1 = dets[:, 1]
13
+ x2 = dets[:, 2]
14
+ y2 = dets[:, 3]
15
+ scores = dets[:, 4]
16
+
17
+ areas = (x2 - x1 + 1) * (y2 - y1 + 1)
18
+ order = scores.argsort()[::-1]
19
+
20
+ keep = []
21
+ while order.size > 0:
22
+ i = order[0]
23
+ keep.append(i)
24
+ xx1 = np.maximum(x1[i], x1[order[1:]])
25
+ yy1 = np.maximum(y1[i], y1[order[1:]])
26
+ xx2 = np.minimum(x2[i], x2[order[1:]])
27
+ yy2 = np.minimum(y2[i], y2[order[1:]])
28
+
29
+ w = np.maximum(0.0, xx2 - xx1 + 1)
30
+ h = np.maximum(0.0, yy2 - yy1 + 1)
31
+ inter = w * h
32
+ ovr = inter / (areas[i] + areas[order[1:]] - inter)
33
+
34
+ inds = np.where(ovr <= thresh)[0]
35
+ order = order[inds + 1]
36
+
37
+ return keep
38
+
39
+
40
+ parser = argparse.ArgumentParser(description="Retinaface")
41
+
42
+ parser.add_argument(
43
+ "--network", default="resnet50", help="Backbone network mobile0.25 or resnet50"
44
+ )
45
+ parser.add_argument(
46
+ "--cpu", action="store_true", default=False, help="Use cpu inference"
47
+ )
48
+ parser.add_argument(
49
+ "--confidence_threshold", default=0.8, type=float, help="confidence_threshold"
50
+ )
51
+ parser.add_argument("--top_k", default=5000, type=int, help="top_k")
52
+ parser.add_argument("--nms_threshold", default=0.2, type=float, help="nms_threshold")
53
+ parser.add_argument("--keep_top_k", default=750, type=int, help="keep_top_k")
54
+ parser.add_argument(
55
+ "-s",
56
+ "--save_image",
57
+ action="store_true",
58
+ default=True,
59
+ help="show detection results",
60
+ )
61
+ parser.add_argument(
62
+ "--vis_thres", default=0.6, type=float, help="visualization_threshold"
63
+ )
64
+ args = parser.parse_args()
65
+
66
+
67
+ def load_model_ort(model_path):
68
+ ort_session = ort.InferenceSession(model_path)
69
+ return ort_session
70
+
71
+
72
+ def retinaface_detect_faces(image, model_path: str, sess=None):
73
+ cfg = {
74
+ "name": "Resnet50",
75
+ "min_sizes": [[16, 32], [64, 128], [256, 512]],
76
+ "steps": [8, 16, 32],
77
+ "variance": [0.1, 0.2],
78
+ "clip": False,
79
+ "loc_weight": 2.0,
80
+ "gpu_train": True,
81
+ "batch_size": 24,
82
+ "ngpu": 4,
83
+ "epoch": 100,
84
+ "decay1": 70,
85
+ "decay2": 90,
86
+ "image_size": 840,
87
+ "pretrain": True,
88
+ "return_layers": {"layer2": 1, "layer3": 2, "layer4": 3},
89
+ "in_channel": 256,
90
+ "out_channel": 256,
91
+ }
92
+
93
+ # Load ONNX model
94
+ if sess is None:
95
+ retinaface = load_model_ort(model_path)
96
+ else:
97
+ retinaface = sess
98
+
99
+ resize = 1
100
+
101
+ # Read and preprocess the image
102
+ img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
103
+ img = np.float32(img_rgb)
104
+
105
+ im_height, im_width, _ = img.shape
106
+ scale = np.array([img.shape[1], img.shape[0], img.shape[1], img.shape[0]])
107
+ img -= (104, 117, 123)
108
+ img = img.transpose(2, 0, 1)
109
+ img = np.expand_dims(img, axis=0)
110
+
111
+ # Run the model
112
+ inputs = {"input": img}
113
+ loc, conf, landms = retinaface.run(None, inputs)
114
+
115
+ # tic = time.time()
116
+ priorbox = PriorBox(cfg, image_size=(im_height, im_width))
117
+ priors = priorbox.forward()
118
+
119
+ prior_data = priors
120
+
121
+ boxes = decode(np.squeeze(loc, axis=0), prior_data, cfg["variance"])
122
+ boxes = boxes * scale / resize
123
+ scores = np.squeeze(conf, axis=0)[:, 1]
124
+
125
+ landms = decode_landm(np.squeeze(landms.data, axis=0), prior_data, cfg["variance"])
126
+
127
+ scale1 = np.array(
128
+ [
129
+ img.shape[3],
130
+ img.shape[2],
131
+ img.shape[3],
132
+ img.shape[2],
133
+ img.shape[3],
134
+ img.shape[2],
135
+ img.shape[3],
136
+ img.shape[2],
137
+ img.shape[3],
138
+ img.shape[2],
139
+ ]
140
+ )
141
+ landms = landms * scale1 / resize
142
+
143
+ # ignore low scores
144
+ inds = np.where(scores > args.confidence_threshold)[0]
145
+ boxes = boxes[inds]
146
+ landms = landms[inds]
147
+ scores = scores[inds]
148
+
149
+ # keep top-K before NMS
150
+ order = scores.argsort()[::-1][: args.top_k]
151
+ boxes = boxes[order]
152
+ landms = landms[order]
153
+ scores = scores[order]
154
+
155
+ # do NMS
156
+ dets = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=False)
157
+ keep = py_cpu_nms(dets, args.nms_threshold)
158
+ # keep = nms(dets, args.nms_threshold,force_cpu=args.cpu)
159
+ dets = dets[keep, :]
160
+ landms = landms[keep]
161
+
162
+ # keep top-K faster NMS
163
+ dets = dets[: args.keep_top_k, :]
164
+ landms = landms[: args.keep_top_k, :]
165
+
166
+ dets = np.concatenate((dets, landms), axis=1)
167
+ # print("post processing time: {:.4f}s".format(time.time() - tic))
168
+
169
+ return dets, retinaface
170
+
171
+
172
+ if __name__ == "__main__":
173
+ import gradio as gr
174
+
175
+ # Create Gradio interface
176
+ iface = gr.Interface(
177
+ fn=retinaface_detect_faces,
178
+ inputs=[
179
+ gr.Image(
180
+ type="numpy", label="上传图片", height=400
181
+ ), # Set the height to 400
182
+ gr.Textbox(value="./FaceDetector.onnx", label="ONNX模型路径"),
183
+ ],
184
+ outputs=gr.Number(label="检测到的人脸数量"),
185
+ title="人脸检测",
186
+ description="上传图片并提供ONNX模型路径以检测人脸数量。",
187
+ )
188
+
189
+ # Launch the Gradio app
190
+ iface.launch()
hivision/creator/retinaface/prior_box.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import product as product
2
+ import numpy as np
3
+ from math import ceil
4
+
5
+
6
+ class PriorBox(object):
7
+ def __init__(self, cfg, image_size=None):
8
+ super(PriorBox, self).__init__()
9
+ self.min_sizes = cfg["min_sizes"]
10
+ self.steps = cfg["steps"]
11
+ self.clip = cfg["clip"]
12
+ self.image_size = image_size
13
+ self.feature_maps = [
14
+ [ceil(self.image_size[0] / step), ceil(self.image_size[1] / step)]
15
+ for step in self.steps
16
+ ]
17
+ self.name = "s"
18
+
19
+ def forward(self):
20
+ anchors = []
21
+ for k, f in enumerate(self.feature_maps):
22
+ min_sizes = self.min_sizes[k]
23
+ for i, j in product(range(f[0]), range(f[1])):
24
+ for min_size in min_sizes:
25
+ s_kx = min_size / self.image_size[1]
26
+ s_ky = min_size / self.image_size[0]
27
+ dense_cx = [
28
+ x * self.steps[k] / self.image_size[1] for x in [j + 0.5]
29
+ ]
30
+ dense_cy = [
31
+ y * self.steps[k] / self.image_size[0] for y in [i + 0.5]
32
+ ]
33
+ for cy, cx in product(dense_cy, dense_cx):
34
+ anchors += [cx, cy, s_kx, s_ky]
35
+
36
+ output = np.array(anchors).reshape(-1, 4)
37
+
38
+ if self.clip:
39
+ output = np.clip(output, 0, 1)
40
+
41
+ return output
hivision/creator/retinaface/weights/.gitkeep ADDED
File without changes
hivision/creator/retinaface/weights/retinaface-resnet50.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:40f825cf7dd0a88b26fb61db9a3aaedc2cad35162091113f4017b3c26a4f792d
3
+ size 109458296