mirror of
https://github.com/serengil/deepface.git
synced 2025-06-07 03:55:21 +00:00
128 lines
4.7 KiB
Python
128 lines
4.7 KiB
Python
# built-in dependencies
|
|
import os
|
|
from typing import Any, List
|
|
|
|
# 3rd party dependencies
|
|
import cv2
|
|
import numpy as np
|
|
|
|
# project dependencies
|
|
from deepface.commons import weight_utils
|
|
from deepface.models.Detector import Detector, FacialAreaRegion
|
|
from deepface.commons.logger import Logger
|
|
|
|
logger = Logger()
|
|
|
|
|
|
class YuNetClient(Detector):
|
|
def __init__(self):
|
|
self.model = self.build_model()
|
|
|
|
def build_model(self) -> Any:
|
|
"""
|
|
Build a yunet detector model
|
|
Returns:
|
|
model (Any)
|
|
"""
|
|
|
|
opencv_version = cv2.__version__.split(".")
|
|
if not len(opencv_version) >= 2:
|
|
raise ValueError(
|
|
f"OpenCv's version must have major and minor values but it is {opencv_version}"
|
|
)
|
|
|
|
opencv_version_major = int(opencv_version[0])
|
|
opencv_version_minor = int(opencv_version[1])
|
|
|
|
if opencv_version_major < 4 or (opencv_version_major == 4 and opencv_version_minor < 8):
|
|
# min requirement: https://github.com/opencv/opencv_zoo/issues/172
|
|
raise ValueError(f"YuNet requires opencv-python >= 4.8 but you have {cv2.__version__}")
|
|
|
|
# pylint: disable=C0301
|
|
weight_file = weight_utils.download_weights_if_necessary(
|
|
file_name="face_detection_yunet_2023mar.onnx",
|
|
source_url="https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx",
|
|
)
|
|
|
|
try:
|
|
face_detector = cv2.FaceDetectorYN_create(weight_file, "", (0, 0))
|
|
except Exception as err:
|
|
raise ValueError(
|
|
"Exception while calling opencv.FaceDetectorYN_create module."
|
|
+ "This is an optional dependency."
|
|
+ "You can install it as pip install opencv-contrib-python."
|
|
) from err
|
|
return face_detector
|
|
|
|
def detect_faces(self, img: np.ndarray) -> List[FacialAreaRegion]:
|
|
"""
|
|
Detect and align face with yunet
|
|
|
|
Args:
|
|
img (np.ndarray): pre-loaded image as numpy array
|
|
|
|
Returns:
|
|
results (List[FacialAreaRegion]): A list of FacialAreaRegion objects
|
|
"""
|
|
# FaceDetector.detect_faces does not support score_threshold parameter.
|
|
# We can set it via environment variable.
|
|
score_threshold = float(os.environ.get("yunet_score_threshold", "0.9"))
|
|
resp = []
|
|
faces = []
|
|
height, width = img.shape[0], img.shape[1]
|
|
# resize image if it is too large (Yunet fails to detect faces on large input sometimes)
|
|
# I picked 640 as a threshold because it is the default value of max_size in Yunet.
|
|
resized = False
|
|
r = 1 # resize factor
|
|
if height > 640 or width > 640:
|
|
r = 640.0 / max(height, width)
|
|
img = cv2.resize(img, (int(width * r), int(height * r)))
|
|
height, width = img.shape[0], img.shape[1]
|
|
resized = True
|
|
self.model.setInputSize((width, height))
|
|
self.model.setScoreThreshold(score_threshold)
|
|
_, faces = self.model.detect(img)
|
|
if faces is None:
|
|
return resp
|
|
for face in faces:
|
|
# pylint: disable=W0105
|
|
"""
|
|
The detection output faces is a two-dimension array of type CV_32F,
|
|
whose rows are the detected face instances, columns are the location
|
|
of a face and 5 facial landmarks.
|
|
The format of each row is as follows:
|
|
x1, y1, w, h, x_re, y_re, x_le, y_le, x_nt, y_nt,
|
|
x_rcm, y_rcm, x_lcm, y_lcm,
|
|
where x1, y1, w, h are the top-left coordinates, width and height of
|
|
the face bounding box,
|
|
{x, y}_{re, le, nt, rcm, lcm} stands for the coordinates of right eye,
|
|
left eye, nose tip, the right corner and left corner of the mouth respectively.
|
|
"""
|
|
(x, y, w, h, x_le, y_le, x_re, y_re) = list(map(int, face[:8]))
|
|
|
|
# YuNet returns negative coordinates if it thinks part of the detected face
|
|
# is outside the frame.
|
|
x = max(x, 0)
|
|
y = max(y, 0)
|
|
if resized:
|
|
x, y, w, h = int(x / r), int(y / r), int(w / r), int(h / r)
|
|
x_re, y_re, x_le, y_le = (
|
|
int(x_re / r),
|
|
int(y_re / r),
|
|
int(x_le / r),
|
|
int(y_le / r),
|
|
)
|
|
confidence = float(face[-1])
|
|
|
|
facial_area = FacialAreaRegion(
|
|
x=x,
|
|
y=y,
|
|
w=w,
|
|
h=h,
|
|
confidence=confidence,
|
|
left_eye=(x_re, y_re),
|
|
right_eye=(x_le, y_le),
|
|
)
|
|
resp.append(facial_area)
|
|
return resp
|