HivisionIDPhotos / demo /processor.py
TheEeeeLin's picture
update
9b2289b
raw
history blame
16.2 kB
import numpy as np
from hivision import IDCreator
from hivision.error import FaceError, APIError
from hivision.utils import (
add_background,
resize_image_to_kb,
add_watermark,
save_image_dpi_to_bytes,
)
from hivision.creator.layout_calculator import (
generate_layout_photo,
generate_layout_image,
)
from hivision.creator.choose_handler import choose_handler
from demo.utils import range_check
import gradio as gr
import os
import time
from demo.locales import LOCALES
class IDPhotoProcessor:
def process(
self,
input_image,
mode_option,
size_list_option,
color_option,
render_option,
image_kb_options,
custom_color_R,
custom_color_G,
custom_color_B,
custom_size_height,
custom_size_width,
custom_image_kb,
language,
matting_model_option,
watermark_option,
watermark_text,
watermark_text_color,
watermark_text_size,
watermark_text_opacity,
watermark_text_angle,
watermark_text_space,
face_detect_option,
head_measure_ratio=0.2,
top_distance_max=0.12,
whitening_strength=0,
image_dpi_option=False,
custom_image_dpi=None,
brightness_strength=0,
contrast_strength=0,
sharpen_strength=0,
saturation_strength=0,
face_alignment_option=False,
):
# 初始化参数
top_distance_min = top_distance_max - 0.02
# 得到render_option在LOCALES["render_mode"][language]["choices"]中的索引
render_option_index = LOCALES["render_mode"][language]["choices"].index(
render_option
)
idphoto_json = self._initialize_idphoto_json(
mode_option, color_option, render_option_index, image_kb_options
)
# 处理尺寸模式
size_result = self._process_size_mode(
idphoto_json,
language,
size_list_option,
custom_size_height,
custom_size_width,
)
if isinstance(size_result, list):
return size_result # 返回错误信息
# 处理颜色模式
self._process_color_mode(
idphoto_json,
language,
color_option,
custom_color_R,
custom_color_G,
custom_color_B,
)
# 如果设置了自定义KB大小
if (
idphoto_json["image_kb_mode"]
== LOCALES["image_kb"][language]["choices"][-1]
):
idphoto_json["custom_image_kb"] = custom_image_kb
# 如果设置了自定义DPI大小
if image_dpi_option == LOCALES["image_dpi"][language]["choices"][-1]:
idphoto_json["custom_image_dpi"] = custom_image_dpi
# 创建IDCreator实例并设置处理器
creator = IDCreator()
choose_handler(creator, matting_model_option, face_detect_option)
# 生成证件照
try:
result = self._generate_id_photo(
creator,
input_image,
idphoto_json,
language,
head_measure_ratio,
top_distance_max,
top_distance_min,
whitening_strength,
brightness_strength,
contrast_strength,
sharpen_strength,
saturation_strength,
face_alignment_option,
)
except (FaceError, APIError):
return self._handle_photo_generation_error(language)
# 后处理生成的照片
return self._process_generated_photo(
result,
idphoto_json,
language,
watermark_option,
watermark_text,
watermark_text_size,
watermark_text_opacity,
watermark_text_angle,
watermark_text_space,
watermark_text_color,
)
def _initialize_idphoto_json(
self,
mode_option,
color_option,
render_option,
image_kb_options,
):
"""初始化idphoto_json字典"""
return {
"size_mode": mode_option,
"color_mode": color_option,
"render_mode": render_option,
"image_kb_mode": image_kb_options,
"custom_image_kb": None,
"custom_image_dpi": None,
}
def _process_size_mode(
self,
idphoto_json,
language,
size_list_option,
custom_size_height,
custom_size_width,
):
"""处理尺寸模式"""
if idphoto_json["size_mode"] == LOCALES["size_mode"][language]["choices"][0]:
idphoto_json["size"] = LOCALES["size_list"][language]["develop"][
size_list_option
]
elif idphoto_json["size_mode"] == LOCALES["size_mode"][language]["choices"][2]:
id_height, id_width = int(custom_size_height), int(custom_size_width)
if (
id_height < id_width
or min(id_height, id_width) < 100
or max(id_height, id_width) > 1800
):
return self._create_error_response(language)
idphoto_json["size"] = (id_height, id_width)
else:
idphoto_json["size"] = (None, None)
def _process_color_mode(
self,
idphoto_json,
language,
color_option,
custom_color_R,
custom_color_G,
custom_color_B,
):
"""处理颜色模式"""
if idphoto_json["color_mode"] == LOCALES["bg_color"][language]["choices"][-1]:
idphoto_json["color_bgr"] = tuple(
map(range_check, [custom_color_R, custom_color_G, custom_color_B])
)
else:
hex_color = LOCALES["bg_color"][language]["develop"][color_option]
idphoto_json["color_bgr"] = tuple(
int(hex_color[i : i + 2], 16) for i in (0, 2, 4)
)
def _generate_id_photo(
self,
creator: IDCreator,
input_image,
idphoto_json,
language,
head_measure_ratio,
top_distance_max,
top_distance_min,
whitening_strength,
brightness_strength,
contrast_strength,
sharpen_strength,
saturation_strength,
face_alignment_option,
):
"""生成证件照"""
change_bg_only = (
idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]
)
return creator(
input_image,
change_bg_only=change_bg_only,
size=idphoto_json["size"],
head_measure_ratio=head_measure_ratio,
head_top_range=(top_distance_max, top_distance_min),
whitening_strength=whitening_strength,
brightness_strength=brightness_strength,
contrast_strength=contrast_strength,
sharpen_strength=sharpen_strength,
saturation_strength=saturation_strength,
face_alignment=face_alignment_option,
)
def _handle_photo_generation_error(self, language):
"""处理照片生成错误"""
return [gr.update(value=None) for _ in range(4)] + [
gr.update(visible=False),
gr.update(
value=LOCALES["notification"][language]["face_error"], visible=True
),
None,
]
def _process_generated_photo(
self,
result,
idphoto_json,
language,
watermark_option,
watermark_text,
watermark_text_size,
watermark_text_opacity,
watermark_text_angle,
watermark_text_space,
watermark_text_color,
):
"""处理生成的照片"""
result_image_standard, result_image_hd, _, _, _, _ = result
result_image_standard_png = np.uint8(result_image_standard)
result_image_hd_png = np.uint8(result_image_hd)
# 渲染背景
result_image_standard, result_image_hd = self._render_background(
result_image_standard, result_image_hd, idphoto_json
)
# 生成排版照片
result_image_layout, result_image_layout_gr = self._generate_image_layout(
idphoto_json,
result_image_standard,
language,
watermark_option,
watermark_text,
watermark_text_size,
watermark_text_opacity,
watermark_text_angle,
watermark_text_space,
watermark_text_color,
)
# 添加水印
if watermark_option == LOCALES["watermark_switch"][language]["choices"][1]:
result_image_standard, result_image_hd = self._add_watermark(
result_image_standard,
result_image_hd,
watermark_text,
watermark_text_size,
watermark_text_opacity,
watermark_text_angle,
watermark_text_space,
watermark_text_color,
)
# 调整图片大小
output_image_path = self._resize_image_if_needed(
result_image_standard,
result_image_hd,
result_image_layout,
idphoto_json,
)
return self._create_response(
result_image_standard,
result_image_hd,
result_image_standard_png,
result_image_hd_png,
result_image_layout_gr,
output_image_path,
)
def _render_background(self, result_image_standard, result_image_hd, idphoto_json):
"""渲染背景"""
render_modes = {0: "pure_color", 1: "updown_gradient", 2: "center_gradient"}
render_mode = render_modes[idphoto_json["render_mode"]]
result_image_standard = np.uint8(
add_background(
result_image_standard, bgr=idphoto_json["color_bgr"], mode=render_mode
)
)
result_image_hd = np.uint8(
add_background(
result_image_hd, bgr=idphoto_json["color_bgr"], mode=render_mode
)
)
return result_image_standard, result_image_hd
def _generate_image_layout(
self,
idphoto_json,
result_image_standard,
language,
watermark_option,
watermark_text,
watermark_text_size,
watermark_text_opacity,
watermark_text_angle,
watermark_text_space,
watermark_text_color,
):
"""生成排版照片"""
if idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]:
return gr.update(visible=False)
typography_arr, typography_rotate = generate_layout_photo(
input_height=idphoto_json["size"][0],
input_width=idphoto_json["size"][1],
)
image = result_image_standard
if watermark_option == LOCALES["watermark_switch"][language]["choices"][1]:
image = add_watermark(
image=image,
text=watermark_text,
size=watermark_text_size,
opacity=watermark_text_opacity,
angle=watermark_text_angle,
space=watermark_text_space,
color=watermark_text_color,
)
result_image_layout = generate_layout_image(
image,
typography_arr,
typography_rotate,
height=idphoto_json["size"][0],
width=idphoto_json["size"][1],
)
return result_image_layout, gr.update(
value=result_image_layout,
visible=True,
)
def _add_watermark(
self,
result_image_standard,
result_image_hd,
watermark_text,
watermark_text_size,
watermark_text_opacity,
watermark_text_angle,
watermark_text_space,
watermark_text_color,
):
"""添加水印"""
watermark_params = {
"text": watermark_text,
"size": watermark_text_size,
"opacity": watermark_text_opacity,
"angle": watermark_text_angle,
"space": watermark_text_space,
"color": watermark_text_color,
}
result_image_standard = add_watermark(
image=result_image_standard, **watermark_params
)
result_image_hd = add_watermark(image=result_image_hd, **watermark_params)
return result_image_standard, result_image_hd
def _resize_image_if_needed(
self,
result_image_standard,
result_image_hd,
result_image_layout,
idphoto_json,
):
"""如果需要,调整图片大小"""
# 设置输出路径
base_path = os.path.join(
os.path.dirname(os.path.dirname(__file__)), "demo/kb_output"
)
timestamp = int(time.time())
output_paths = {
"standard": f"{base_path}/{timestamp}_standard",
"hd": f"{base_path}/{timestamp}_hd",
"layout": f"{base_path}/{timestamp}_layout",
}
# 获取自定义的KB和DPI值
custom_kb = idphoto_json.get("custom_image_kb")
custom_dpi = idphoto_json.get("custom_image_dpi", 300)
# 处理同时有自定义KB和DPI的情况
if custom_kb and custom_dpi:
# 为所有输出路径添加DPI信息
for key in output_paths:
output_paths[key] += f"_{custom_dpi}dpi.jpg"
# 为标准图像添加KB信息
output_paths["standard"] = output_paths["standard"].replace(
".jpg", f"_{custom_kb}kb.jpg"
)
# 调整标准图像大小并保存
resize_image_to_kb(
result_image_standard,
output_paths["standard"],
custom_kb,
dpi=custom_dpi,
)
# 保存高清图像和排版图像
save_image_dpi_to_bytes(result_image_hd, output_paths["hd"], dpi=custom_dpi)
save_image_dpi_to_bytes(
result_image_layout, output_paths["layout"], dpi=custom_dpi
)
return list(output_paths.values())
# 只有自定义DPI的情况
elif custom_dpi:
for key in output_paths:
output_paths[key] += f"_{custom_dpi}dpi.jpg"
# 保存所有图像,使用自定义DPI
save_image_dpi_to_bytes(
locals()[f"result_image_{key}"], output_paths[key], dpi=custom_dpi
)
return list(output_paths.values())
# 只有自定义KB的情况
elif custom_kb:
output_paths["standard"] += f"_{custom_kb}kb.jpg"
# 只调整标准图像大小并保存
resize_image_to_kb(
result_image_standard,
output_paths["standard"],
custom_kb,
dpi=300,
)
return [output_paths["standard"]]
# 如果没有自定义设置,返回None
return None
def _create_response(
self,
result_image_standard,
result_image_hd,
result_image_standard_png,
result_image_hd_png,
result_layout_image,
output_image_path,
):
"""创建响应"""
response = [
result_image_standard,
result_image_hd,
result_image_standard_png,
result_image_hd_png,
result_layout_image,
gr.update(visible=False),
]
if output_image_path:
response.append(gr.update(visible=True, value=output_image_path))
else:
response.append(gr.update(visible=False))
return response
def _create_error_response(self, language):
"""创建错误响应"""
return [gr.update(value=None) for _ in range(4)] + [
None,
gr.update(
value=LOCALES["size_mode"][language]["custom_size_eror"], visible=True
),
None,
]