Spaces:
Running
Running
TheEeeeLin
commited on
Commit
•
402b504
1
Parent(s):
a5eebe4
update retinaface
Browse files- .gitattributes +1 -0
- .gitignore +2 -1
- app.py +28 -873
- demo/assets/color_list_CN.csv +6 -0
- demo/assets/color_list_EN.csv +6 -0
- demo/{size_list_CN.csv → assets/size_list_CN.csv} +0 -0
- demo/{size_list_EN.csv → assets/size_list_EN.csv} +0 -0
- demo/config.py +19 -0
- demo/locals.py +271 -0
- demo/processor.py +286 -0
- demo/ui.py +493 -0
- demo/utils.py +42 -0
- hivision/creator/choose_handler.py +13 -3
- hivision/creator/face_detector.py +51 -1
- hivision/creator/retinaface/__init__.py +1 -0
- hivision/creator/retinaface/box_utils.py +57 -0
- hivision/creator/retinaface/inference.py +190 -0
- hivision/creator/retinaface/prior_box.py +41 -0
- hivision/creator/retinaface/weights/.gitkeep +0 -0
- hivision/creator/retinaface/weights/retinaface-resnet50.onnx +3 -0
.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 |
-
|
16 |
-
from
|
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 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
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 |
-
|
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 |
-
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
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
|