Spaces:
Running
Running
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
|