Spaces:
Running
Running
File size: 7,778 Bytes
ca46a75 402b504 ca46a75 23cd1cf 402b504 23cd1cf ca46a75 23cd1cf 9b2289b 23cd1cf ca46a75 402b504 ca46a75 23cd1cf ca46a75 23cd1cf ca46a75 9b2289b 23cd1cf ca46a75 9b2289b ca46a75 23cd1cf ca46a75 23cd1cf 9b2289b 23cd1cf 9b2289b 23cd1cf 9b2289b 23cd1cf 06fbec3 23cd1cf 9b2289b 23cd1cf 9b2289b 23cd1cf 9b2289b 23cd1cf 402b504 9b2289b 402b504 9b2289b 402b504 9b2289b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2024/9/5 19:32
@File: face_detector.py
@IDE: pycharm
@Description:
人脸检测器
"""
try:
from mtcnnruntime import MTCNN
except ImportError:
raise ImportError(
"Please install mtcnn-runtime by running `pip install mtcnn-runtime`"
)
from .context import Context
from hivision.error import FaceError, APIError
from hivision.utils import resize_image_to_kb_base64
from hivision.creator.retinaface import retinaface_detect_faces
import requests
import cv2
import os
import numpy as np
mtcnn = None
base_dir = os.path.dirname(os.path.abspath(__file__))
RETINAFCE_SESS = None
def detect_face_mtcnn(ctx: Context, scale: int = 2):
"""
基于MTCNN模型的人脸检测处理器,只进行人脸数量的检测
: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,
)
# landmarks 是 5 个关键点,分别是左眼、右眼、鼻子、左嘴角、右嘴角,
faces, landmarks = mtcnn.detect(image, thresholds=[0.8, 0.8, 0.8])
# print(len(faces))
if len(faces) != 1:
# 保险措施,如果检测到多个人脸或者没有人脸,用原图再检测一次
faces, landmarks = 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))
# 计算人脸坐标
left = faces[0][0]
top = faces[0][1]
width = faces[0][2] - left + 1
height = faces[0][3] - top + 1
ctx.face["rectangle"] = (left, top, width, height)
# 根据landmarks计算人脸偏转角度,以眼睛为标准,计算的人脸偏转角度,用于人脸矫正
# 示例landmarks [106.37181 150.77415 127.21012 108.369156 144.61522 105.24723 107.45625 133.62355 151.24269 153.34407 ]
landmarks = landmarks[0]
left_eye = np.array([landmarks[0], landmarks[5]])
right_eye = np.array([landmarks[1], landmarks[6]])
dy = right_eye[1] - left_eye[1]
dx = right_eye[0] - left_eye[0]
roll_angle = np.degrees(np.arctan2(dy, dx))
ctx.face["roll_angle"] = roll_angle
def detect_face_face_plusplus(ctx: Context):
"""
基于Face++ API接口的人脸检测处理器,只进行人脸数量的检测
:param ctx: 上下文,此时已获取到原始图和抠图结果,但是我们只需要原始图
:param scale: 最大边长缩放比例,原图:缩放图 = 1:scale
:raise FaceError: 人脸检测错误,多个人脸或者没有人脸
"""
url = "https://api-cn.faceplusplus.com/facepp/v3/detect"
api_key = os.getenv("FACE_PLUS_API_KEY")
api_secret = os.getenv("FACE_PLUS_API_SECRET")
print("调用了face++")
image = ctx.origin_image
# 将图片转为 base64, 且不大于2MB(Face++ API接口限制)
image_base64 = resize_image_to_kb_base64(image, 2000, mode="max")
files = {
"api_key": (None, api_key),
"api_secret": (None, api_secret),
"image_base64": (None, image_base64),
"return_landmark": (None, "1"),
"return_attributes": (None, "headpose"),
}
# 发送 POST 请求
response = requests.post(url, files=files)
# 获取响应状态码
status_code = response.status_code
response_json = response.json()
if status_code == 200:
face_num = response_json["face_num"]
if face_num == 1:
face_rectangle = response_json["faces"][0]["face_rectangle"]
# 获取人脸关键点
# landmarks = response_json["faces"][0]["landmark"]
# print("face++ landmarks", landmarks)
# headpose 是一个字典,包含俯仰角(pitch)、偏航角(yaw)和滚转角(roll)
# headpose示例 {'pitch_angle': 6.997899, 'roll_angle': 1.8011835, 'yaw_angle': 5.043002}
headpose = response_json["faces"][0]["attributes"]["headpose"]
# 以眼睛为标准,计算的人脸偏转角度,用于人脸矫正
roll_angle = headpose["roll_angle"] / 2
ctx.face["rectangle"] = (
face_rectangle["left"],
face_rectangle["top"],
face_rectangle["width"],
face_rectangle["height"],
)
ctx.face["roll_angle"] = roll_angle
else:
raise FaceError(
"Expected 1 face, but got {}".format(face_num), len(face_num)
)
elif status_code == 401:
raise APIError(
f"Face++ Status code {status_code} Authentication error: API key and secret do not match.",
status_code,
)
elif status_code == 403:
reason = response_json.get("error_message", "Unknown authorization error.")
raise APIError(
f"Authorization error: {reason}",
status_code,
)
elif status_code == 400:
error_message = response_json.get("error_message", "Bad request.")
raise APIError(
f"Bad request error: {error_message}",
status_code,
)
elif status_code == 413:
raise APIError(
f"Face++ Status code {status_code} Request entity too large: The image exceeds the 2MB limit.",
status_code,
)
def detect_face_retinaface(ctx: Context):
"""
基于RetinaFace模型的人脸检测处理器,只进行人脸数量的检测
:param ctx: 上下文,此时已获取到原始图和抠图结果,但是我们只需要原始图
:raise FaceError: 人脸检测错误,多个人脸或者没有人脸
"""
from time import time
global RETINAFCE_SESS
if RETINAFCE_SESS is None:
print("首次加载RetinaFace模型...")
# 计算用时
tic = time()
faces_dets, sess = retinaface_detect_faces(
ctx.origin_image,
os.path.join(base_dir, "retinaface/weights/retinaface-resnet50.onnx"),
sess=None,
)
RETINAFCE_SESS = sess
print("首次RetinaFace模型推理用时: {:.4f}s".format(time() - tic))
else:
tic = time()
faces_dets, _ = retinaface_detect_faces(
ctx.origin_image,
os.path.join(base_dir, "retinaface/weights/retinaface-resnet50.onnx"),
sess=RETINAFCE_SESS,
)
print("二次RetinaFace模型推理用时: {:.4f}s".format(time() - tic))
faces_num = len(faces_dets)
faces_landmarks = []
for face_det in faces_dets:
faces_landmarks.append(face_det[5:])
if faces_num != 1:
raise FaceError("Expected 1 face, but got {}".format(faces_num), faces_num)
face_det = faces_dets[0]
ctx.face["rectangle"] = (
face_det[0],
face_det[1],
face_det[2] - face_det[0] + 1,
face_det[3] - face_det[1] + 1,
)
# 计算roll_angle
face_landmarks = faces_landmarks[0]
# print("face_landmarks", face_landmarks)
left_eye = np.array([face_landmarks[0], face_landmarks[1]])
right_eye = np.array([face_landmarks[2], face_landmarks[3]])
dy = right_eye[1] - left_eye[1]
dx = right_eye[0] - left_eye[0]
roll_angle = np.degrees(np.arctan2(dy, dx))
ctx.face["roll_angle"] = roll_angle
|