refactor:main

This commit is contained in:
KAAANG
2024-09-05 21:20:17 +08:00
parent b46772d68e
commit 14fc91919c
18 changed files with 622 additions and 103 deletions
+2 -2
View File
@@ -1,9 +1,9 @@
import os
import gradio as gr
import onnxruntime
from src.face_judgement_align import IDphotos_create
from hivision.creator.face_judgement_align import IDphotos_create
from hivisionai.hycv.vision import add_background
from src.layoutCreate import generate_layout_photo, generate_layout_image
from hivision.creator.layoutCreate import generate_layout_photo, generate_layout_image
import pathlib
import numpy as np
from utils.image_utils import resize_image_to_kb
-10
View File
@@ -1,10 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2024/9/5 16:45
@File: __init__.py
@IDE: pycharm
@Description:
创建证件照
"""
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2024/9/5 16:45
@File: __init__.py
@IDE: pycharm
@Description:
创建证件照
"""
import numpy as np
from typing import Tuple
import hivision.creator.utils as U
from .context import Context, ContextHandler, Params, Result
from .face_detector import detect_face
from .photo_adjuster import adjust_photo
class IDCreator:
"""
证件照创建类,包含完整的证件照流程
"""
def __init__(self):
# 回调时机
self.before_all: ContextHandler = None
"""
在所有处理之前,此时图像已经被 resize 到最大边长为 2000
"""
self.after_matting: ContextHandler = None
"""
在抠图之后,ctx.matting_image 被赋值
"""
self.after_detect: ContextHandler = None
"""
在人脸检测之后,ctx.face 被赋值,如果为仅换底,则不会执行此回调
"""
self.after_adjust: ContextHandler = None
"""
在人脸调整之后,ctx.face 被赋值,如果为仅换底,则不会执行此回调
"""
self.after_all: ContextHandler = None
"""
在所有处理之后,此时 ctx.result被赋值
"""
# 处理者
self.matting_handler: ContextHandler = None
self.detection_handler: ContextHandler = None
# 上下文
self.ctx = None
def __call__(
self,
image: np.ndarray,
size: Tuple[int, int] = (413, 295),
change_bg_only: bool = False,
head_measure_ratio: float = 0.2,
head_height_ratio: float = 0.45,
head_top_range: float = (0.12, 0.1),
) -> Result:
"""
证件照处理函数
:param image: 输入图像
:param change_bg_only: 是否只需要换底
:param size: 输出的图像大小(h,w)
:param head_measure_ratio: 人脸面积与全图面积的期望比值
:param head_height_ratio: 人脸中心处在全图高度的比例期望值
:param head_top_range: 头距离顶部的比例(max,min)
:return: 返回处理后的证件照和一系列参数
"""
# 0.初始化上下文
params = Params(
size=size,
change_bg_only=change_bg_only,
head_measure_ratio=head_measure_ratio,
head_height_ratio=head_height_ratio,
head_top_range=head_top_range,
)
self.ctx = Context(params)
ctx = self.ctx
ctx.processing_image = image
ctx.processing_image = U.resize_image_esp(
ctx.processing_image, 2000
) # 将输入图片 resize 到最大边长为 2000
self.before_all and self.before_all(ctx)
# 1. 人像抠图
self.matting_handler(ctx)
ctx.matting_image = ctx.processing_image.copy()
self.after_matting and self.after_matting(ctx)
# 2. 人脸检测
ctx.face = detect_face(ctx)
self.after_detect and self.after_detect(ctx)
# 3. 图像调整
result_image_hd, result_image_standard, clothing_params, typography_params = (
adjust_photo(ctx)
)
ctx.result = Result(
standard=result_image_standard,
hd=result_image_hd,
clothing_params=clothing_params,
typography_params=typography_params,
)
self.after_adjust and self.after_adjust(ctx)
return ctx.result
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2024/9/5 19:20
@File: context.py
@IDE: pycharm
@Description:
证件照创建上下文类,用于同步信息
"""
from typing import Optional, Callable, Tuple
import numpy as np
class Params:
def __init__(
self,
size: Tuple[int, int] = (413, 295),
change_bg_only: bool = False,
head_measure_ratio: float = 0.2,
head_height_ratio: float = 0.45,
head_top_range: float = (0.12, 0.1),
):
self.__size = size
self.__change_bg_only = change_bg_only
self.__head_measure_ratio = head_measure_ratio
self.__head_height_ratio = head_height_ratio
self.__head_top_range = head_top_range
@property
def size(self):
return self.__size
@property
def change_bg_only(self):
return self.__change_bg_only
@property
def head_measure_ratio(self):
return self.__head_measure_ratio
@property
def head_height_ratio(self):
return self.__head_height_ratio
@property
def head_top_range(self):
return self.__head_top_range
class Result:
def __init__(
self,
standard: np.ndarray,
hd: np.ndarray,
clothing_params: dict,
typography_params: dict,
):
self.standard = standard
self.hd = hd
self.clothing_params = clothing_params
self.typography_params = typography_params
class Context:
def __init__(self, params: Params):
self.params: Params = params
"""
证件照处理参数
"""
self.origin_image: Optional[np.ndarray] = None
"""
输入的原始图像,处理时会进行resize,长宽不一定等于输入图像
"""
self.processing_image: Optional[np.ndarray] = None
"""
当前正在处理的图像
"""
self.matting_image: Optional[np.ndarray] = None
"""
人像抠图结果
"""
self.face: Optional[Tuple[int, int, int, int, float]] = None
"""
人脸检测结果,大于一个人脸时已在上层抛出异常
元组长度为5,包含 x1, y1, x2, y2, score 的坐标, (x1, y1)为左上角坐标,(x2, y2)为右下角坐标, score为置信度, 最大值为1
"""
self.result: Optional[Result] = None
"""
证件照处理结果
"""
ContextHandler = Optional[Callable[[Context], None]]
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2024/9/5 19:32
@File: face_detector.py
@IDE: pycharm
@Description:
人脸检测器
"""
from mtcnnruntime import MTCNN
from .context import Context
from hivision.error import FaceError
import cv2
mtcnn = None
def detect_face(ctx: Context, scale: int = 2):
"""
人脸检测处理者,只进行人脸数量的检测
:param ctx: 上下文,此时已获取到原始图和抠图结果,但是我们只需要原始图
:param scale: 最大边长缩放比例,原图:缩放图 = 1:scale
:raise FaceError: 人脸检测错误,多个人脸或者没有人脸
"""
global mtcnn
if mtcnn is None:
mtcnn = MTCNN()
image = cv2.resize(
ctx.origin_image,
(ctx.origin_image.shape[1] // scale, ctx.origin_image.shape[0] // scale),
interpolation=cv2.INTER_AREA,
)
faces, _ = mtcnn.detect(image)
if len(faces) != 1:
# 保险措施,如果检测到多个人脸或者没有人脸,用原图再检测一次
faces, _ = mtcnn.detect(ctx.origin_image)
else:
for item, param in enumerate(faces[0]):
faces[0][item] = param * 2
if len(faces) != 1:
raise FaceError("Expected 1 face, but got {}".format(len(faces)), len(faces))
ctx.face = (faces[0][0], faces[0][1], faces[0][2], faces[0][3], faces[0][4])
@@ -12,16 +12,16 @@ from hivisionai.hycv.vision import (
rotate_bound_4channels,
)
import onnxruntime
from src.error import IDError
from src.imageTransform import (
from hivision.error import FaceError
from hivision.creator.imageTransform import (
standard_photo_resize,
hollowOutFix,
get_modnet_matting,
draw_picture_dots,
detect_distance,
)
from src.layoutCreate import generate_layout_photo
from src.move_image import move
from hivision.creator.layoutCreate import generate_layout_photo
from hivision.creator.move_image import move
testImages = []
@@ -89,25 +89,12 @@ def face_number_and_angle_detection(input_image):
- landmark: list人脸关键点信息
"""
# face++ 人脸检测
# input_image_bytes = CV2Bytes.cv2_byte(input_image, ".jpg")
# face_num, face_rectangle, landmarks, headpose = megvii_face_detector(input_image_bytes)
# print(face_rectangle)
faces, landmarks = face_detect_mtcnn(input_image)
face_num = len(faces)
# 排除不合人脸数目要求(必须是 1)的照片
if face_num == 0 or face_num >= 2:
if face_num == 0:
status_id_ = "1101"
else:
status_id_ = "1102"
raise IDError(
f"人脸检测出错!检测出了{face_num}张人脸",
face_num=face_num,
status_id=status_id_,
)
raise FaceError(f"人脸检测出错!检测出了{face_num}张人脸", face_num=face_num)
# 获得人脸定位坐标
face_rectangle = []
@@ -328,10 +315,7 @@ def idphoto_cutting(
standard_size,
head_height_ratio,
origin_png_image,
origin_png_image_pre,
rotation_params,
align=False,
IS_DEBUG=False,
top_distance_max=0.12,
top_distance_min=0.10,
):
@@ -435,8 +419,6 @@ def idphoto_cutting(
y_top, y_bottom, x_left, x_right = get_box_pro(
cut_image.astype(np.uint8), model=2, correction_factor=0
) # 得到 cut_image 中人像的上下左右距离信息
if IS_DEBUG:
testImages.append(["firstCut", cut_image])
# Step5. 判定 cut_image 中的人像是否处于合理的位置,若不合理,则处理数据以便之后调整位置
# 检测人像与裁剪框左边或右边是否存在空隙
@@ -470,8 +452,6 @@ def idphoto_cutting(
y2 - cut_value_top + status_top * move_value,
origin_png_image,
)
if IS_DEBUG:
testImages.append(["result_image_pre", result_image])
# 换装参数准备
relative_x = x - (x1 + x_left)
@@ -665,37 +645,3 @@ def IDphotos_create(
clothing_params["h"],
1,
)
if __name__ == "__main__":
HY_HUMAN_MATTING_WEIGHTS_PATH = "./hivision_modnet.onnx"
sess = onnxruntime.InferenceSession(HY_HUMAN_MATTING_WEIGHTS_PATH)
input_image = cv2.imread("test.jpg")
(
result_image_hd,
result_image_standard,
typography_arr,
typography_rotate,
_,
_,
_,
_,
_,
) = IDphotos_create(
input_image,
size=(413, 295),
head_measure_ratio=0.2,
head_height_ratio=0.45,
align=True,
beauty=True,
fd68=None,
human_sess=sess,
oss_image_name="test_tmping.jpg",
user=None,
IS_DEBUG=False,
top_distance_max=0.12,
top_distance_min=0.10,
)
cv2.imwrite("result_image_hd.png", result_image_hd)
+207
View File
@@ -0,0 +1,207 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2024/9/5 20:02
@File: photo_adjuster.py
@IDE: pycharm
@Description:
证件照调整
"""
from .context import Context
import hivision.creator.utils as U
from hivision.creator.imageTransform import (
standard_photo_resize,
)
from hivisionai.hycv.vision import (
resize_image_by_min,
)
import numpy as np
import math
import cv2
from hivision.creator.layoutCreate import generate_layout_photo
def adjust_photo(ctx: Context):
# Step1. 准备人脸参数
face_rect = ctx.face
standard_size = ctx.params.size
params = ctx.params
x, y = face_rect[0], face_rect[1]
w, h = face_rect[2] - x + 1, face_rect[3] - y + 1
height, width = ctx.processing_image.shape[:2]
width_height_ratio = standard_size[0] / standard_size[1]
# Step2. 计算高级参数
face_center = (x + w / 2, y + h / 2) # 面部中心坐标
face_measure = w * h # 面部面积
crop_measure = (
face_measure / params.head_measure_ratio
) # 裁剪框面积:为面部面积的 5 倍
resize_ratio = crop_measure / (standard_size[0] * standard_size[1]) # 裁剪框缩放率
resize_ratio_single = math.sqrt(
resize_ratio
) # 长和宽的缩放率(resize_ratio 的开方)
crop_size = (
int(standard_size[0] * resize_ratio_single),
int(standard_size[1] * resize_ratio_single),
) # 裁剪框大小
# 裁剪框的定位信息
x1 = int(face_center[0] - crop_size[1] / 2)
y1 = int(face_center[1] - crop_size[0] * params.head_height_ratio)
y2 = y1 + crop_size[0]
x2 = x1 + crop_size[1]
# Step3, 裁剪框的调整
cut_image = IDphotos_cut(x1, y1, x2, y2, ctx.processing_image)
cut_image = cv2.resize(cut_image, (crop_size[1], crop_size[0]))
y_top, y_bottom, x_left, x_right = U.get_box(
cut_image.astype(np.uint8), model=2, correction_factor=0
) # 得到 cut_image 中人像的上下左右距离信息
# Step5. 判定 cut_image 中的人像是否处于合理的位置,若不合理,则处理数据以便之后调整位置
# 检测人像与裁剪框左边或右边是否存在空隙
if x_left > 0 or x_right > 0:
status_left_right = 1
cut_value_top = int(
((x_left + x_right) * width_height_ratio) / 2
) # 减去左右,为了保持比例,上下也要相应减少 cut_value_top
else:
status_left_right = 0
cut_value_top = 0
"""
检测人头顶与照片的顶部是否在合适的距离内:
- status==0: 距离合适,无需移动
- status=1: 距离过大,人像应向上移动
- status=2: 距离过小,人像应向下移动
"""
status_top, move_value = U.detect_distance(
y_top - cut_value_top,
crop_size[0],
max=params.head_top_range[0],
min=params.head_top_range[1],
)
# Step6. 对照片的第二轮裁剪
if status_left_right == 0 and status_top == 0:
result_image = cut_image
else:
result_image = IDphotos_cut(
x1 + x_left,
y1 + cut_value_top + status_top * move_value,
x2 - x_right,
y2 - cut_value_top + status_top * move_value,
ctx.processing_image,
)
# 换装参数准备
relative_x = x - (x1 + x_left)
relative_y = y - (y1 + cut_value_top + status_top * move_value)
# Step7. 当照片底部存在空隙时,下拉至底部
result_image, y_high = move(result_image.astype(np.uint8))
relative_y = relative_y + y_high # 更新换装参数
# Step8. 标准照与高清照转换
result_image_standard = standard_photo_resize(result_image, standard_size)
result_image_hd, resize_ratio_max = resize_image_by_min(
result_image, esp=max(600, standard_size[1])
)
# Step9. 参数准备 - 为换装服务
clothing_params = {
"relative_x": relative_x * resize_ratio_max,
"relative_y": relative_y * resize_ratio_max,
"w": w * resize_ratio_max,
"h": h * resize_ratio_max,
}
# Step7. 排版照参数获取
typography_arr, typography_rotate = generate_layout_photo(
input_height=standard_size[0], input_width=standard_size[1]
)
return (
result_image_hd,
result_image_standard,
clothing_params,
{
"arr": typography_arr,
"rotate": typography_rotate,
},
)
def IDphotos_cut(x1, y1, x2, y2, img):
"""
在图片上进行滑动裁剪,输入输出为
输入:一张图片 img,和裁剪框信息 (x1,x2,y1,y2)
输出:裁剪好的图片,然后裁剪框超出了图像范围,那么将用 0 矩阵补位
------------------------------------
x:裁剪框左上的横坐标
y:裁剪框左上的纵坐标
x2:裁剪框右下的横坐标
y2:裁剪框右下的纵坐标
crop_size:裁剪框大小
img:裁剪图像(numpy.array
output_path:裁剪图片的输出路径
------------------------------------
"""
crop_size = (y2 - y1, x2 - x1)
"""
------------------------------------
temp_x_1:裁剪框左边超出图像部分
temp_y_1:裁剪框上边超出图像部分
temp_x_2:裁剪框右边超出图像部分
temp_y_2:裁剪框下边超出图像部分
------------------------------------
"""
temp_x_1 = 0
temp_y_1 = 0
temp_x_2 = 0
temp_y_2 = 0
if y1 < 0:
temp_y_1 = abs(y1)
y1 = 0
if y2 > img.shape[0]:
temp_y_2 = y2
y2 = img.shape[0]
temp_y_2 = temp_y_2 - y2
if x1 < 0:
temp_x_1 = abs(x1)
x1 = 0
if x2 > img.shape[1]:
temp_x_2 = x2
x2 = img.shape[1]
temp_x_2 = temp_x_2 - x2
# 生成一张全透明背景
print("crop_size:", crop_size)
background_bgr = np.full((crop_size[0], crop_size[1]), 255, dtype=np.uint8)
background_a = np.full((crop_size[0], crop_size[1]), 0, dtype=np.uint8)
background = cv2.merge(
(background_bgr, background_bgr, background_bgr, background_a)
)
background[
temp_y_1 : crop_size[0] - temp_y_2, temp_x_1 : crop_size[1] - temp_x_2
] = img[y1:y2, x1:x2]
return background
def move(input_image):
"""
裁剪主函数,输入一张 png 图像,该图像周围是透明的
"""
png_img = input_image # 获取图像
height, width, channels = png_img.shape # 高 y、宽 x
y_low, y_high, _, _ = U.get_box(png_img, model=2) # for 循环
base = np.zeros((y_high, width, channels), dtype=np.uint8) # for 循环
png_img = png_img[0 : height - y_high, :, :] # for 循环
png_img = np.concatenate((base, png_img), axis=0)
return png_img, y_high
+142
View File
@@ -0,0 +1,142 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2024/9/5 19:25
@File: utils.py
@IDE: pycharm
@Description:
通用图像处理工具
"""
import cv2
import numpy as np
def resize_image_esp(input_image, esp=2000):
"""
输入:
input_pathnumpy 图片
esp:限制的最大边长
"""
# resize 函数=>可以让原图压缩到最大边为 esp 的尺寸 (不改变比例)
width = input_image.shape[0]
length = input_image.shape[1]
max_num = max(width, length)
if max_num > esp:
print("Image resizing...")
if width == max_num:
length = int((esp / width) * length)
width = esp
else:
width = int((esp / length) * width)
length = esp
print(length, width)
im_resize = cv2.resize(
input_image, (length, width), interpolation=cv2.INTER_AREA
)
return im_resize
else:
return input_image
def get_box(
image: np.ndarray,
model: int = 1,
correction_factor=None,
thresh: int = 127,
):
"""
本函数能够实现输入一张四通道图像,返回图像中最大连续非透明面积的区域的矩形坐标
本函数将采用 opencv 内置函数来解析整个图像的 mask,并提供一些参数,用于读取图像的位置信息
Args:
image: 四通道矩阵图像
model: 返回值模式
correction_factor: 提供一些边缘扩张接口,输入格式为 list 或者 int:[up, down, left, right]。
举个例子,假设我们希望剪切出的矩形框左边能够偏左 1 个像素,则输入 [0, 0, 1, 0]
如果希望右边偏右 1 个像素,则输入 [0, 0, 0, 1]
如果输入为 int,则默认只会对左右两边做拓展,比如输入 2,则和 [0, 0, 2, 2] 是等效的
thresh: 二值化阈值,为了保持一些羽化效果,thresh 必须要小
Returns:
model 为 1 时,将会返回切割出的矩形框的四个坐标点信息
model 为 2 时,将会返回矩形框四边相距于原图四边的距离
"""
# ------------ 数据格式规范部分 -------------- #
# 输入必须为四通道
if correction_factor is None:
correction_factor = [0, 0, 0, 0]
if not isinstance(image, np.ndarray) or len(cv2.split(image)) != 4:
raise TypeError("输入的图像必须为四通道 np.ndarray 类型矩阵!")
# correction_factor 规范化
if isinstance(correction_factor, int):
correction_factor = [0, 0, correction_factor, correction_factor]
elif not isinstance(correction_factor, list):
raise TypeError("correction_factor 必须为 int 或者 list 类型!")
# ------------ 数据格式规范完毕 -------------- #
# 分离 mask
_, _, _, mask = cv2.split(image)
# mask 二值化处理
_, mask = cv2.threshold(mask, thresh=thresh, maxval=255, type=0)
contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
temp = np.ones(image.shape, np.uint8) * 255
cv2.drawContours(temp, contours, -1, (0, 0, 255), -1)
contours_area = []
for cnt in contours:
contours_area.append(cv2.contourArea(cnt))
idx = contours_area.index(max(contours_area))
x, y, w, h = cv2.boundingRect(contours[idx]) # 框出图像
# ------------ 开始输出数据 -------------- #
height, width, _ = image.shape
y_up = y - correction_factor[0] if y - correction_factor[0] >= 0 else 0
y_down = (
y + h + correction_factor[1]
if y + h + correction_factor[1] < height
else height - 1
)
x_left = x - correction_factor[2] if x - correction_factor[2] >= 0 else 0
x_right = (
x + w + correction_factor[3]
if x + w + correction_factor[3] < width
else width - 1
)
if model == 1:
# model=1,将会返回切割出的矩形框的四个坐标点信息
return [y_up, y_down, x_left, x_right]
elif model == 2:
# model=2, 将会返回矩形框四边相距于原图四边的距离
return [y_up, height - y_down, x_left, width - x_right]
else:
raise EOFError("请选择正确的模式!")
def detect_distance(value, crop_height, max=0.06, min=0.04):
"""
检测人头顶与照片顶部的距离是否在适当范围内。
输入:与顶部的差值
输出:(status, move_value)
status=0 不动
status=1 人脸应向上移动(裁剪框向下移动)
status-2 人脸应向下移动(裁剪框向上移动)
---------------------------------------
value:头顶与照片顶部的距离
crop_height: 裁剪框的高度
max: 距离的最大值
min: 距离的最小值
---------------------------------------
"""
value = value / crop_height # 头顶往上的像素占图像的比例
if min <= value <= max:
return 0, 0
elif value > max:
# 头顶往上的像素比例高于 max
move_value = value - max
move_value = int(move_value * crop_height)
# print("上移{}".format(move_value))
return 1, move_value
else:
# 头顶往上的像素比例低于 min
move_value = min - value
move_value = int(move_value * crop_height)
# print("下移{}".format(move_value))
return -1, move_value
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2024/9/5 18:32
@File: error.py
@IDE: pycharm
@Description:
错误处理
"""
class FaceError(Exception):
def __init__(self, err, face_num):
"""
证件照人脸错误,此时人脸检测失败,可能是没有检测到人脸或者检测到多个人脸
Args:
err: 错误描述
face_num: 告诉此时识别到的人像个数
"""
super().__init__(err)
self.face_num = face_num
+1
View File
@@ -1,3 +1,4 @@
opencv-python>=4.8.1.78
onnxruntime==1.15.0
numpy==1.24.3
mtcnn-runtime
+2 -3
View File
@@ -1,13 +1,12 @@
from fastapi import FastAPI, UploadFile, Form
import onnxruntime
from src.face_judgement_align import IDphotos_create
from src.layoutCreate import generate_layout_photo, generate_layout_image
from hivision.creator.face_judgement_align import IDphotos_create
from hivision.creator.layoutCreate import generate_layout_photo, generate_layout_image
from hivisionai.hycv.vision import add_background
from utils import resize_image_to_kb_base64, hex_to_rgb
import base64
import numpy as np
import cv2
import ast
app = FastAPI()
+2 -2
View File
@@ -4,9 +4,9 @@ import argparse
import numpy as np
import onnxruntime
from utils import resize_image_to_kb, hex_to_rgb
from src.face_judgement_align import IDphotos_create
from hivision.creator.face_judgement_align import IDphotos_create
from hivisionai.hycv.vision import add_background
from src.layoutCreate import generate_layout_photo, generate_layout_image
from hivision.creator.layoutCreate import generate_layout_photo, generate_layout_image
parser = argparse.ArgumentParser(description="HivisionIDPhotos 证件照制作推理程序。")
-27
View File
@@ -1,27 +0,0 @@
"""
@author: cuny
@file: error.py
@time: 2022/4/7 15:50
@description:
定义证件照制作的错误类
"""
from hivisionai.hyService.error import ProcessError
class IDError(ProcessError):
def __init__(self, err, diary=None, face_num=-1, status_id: str = "1500"):
"""
用于报错
Args:
err: 错误描述
diary: 函数运行日志,默认为 None
face_num: 告诉此时识别到的人像个数,如果为 -1 则说明为未知错误
"""
super().__init__(err)
if diary is None:
diary = {}
self.err = err
self.diary = diary
self.face_num = face_num
self.status_id = status_id