mirror of
https://github.com/serengil/deepface.git
synced 2025-06-06 11:35:21 +00:00
Revert "refactor detectors to have default detect_faces method that is based on process_single_image method"
This reverts commit 8c7c2cb9b7505bd17beed69e3bbf16a8b4c8940d.
This commit is contained in:
parent
6a3d14cde7
commit
da8f644c79
@ -1,8 +1,37 @@
|
||||
from typing import List, Tuple, Optional, Union
|
||||
from abc import ABC
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
import numpy as np
|
||||
|
||||
# Notice that all facial detector models must be inherited from this class
|
||||
|
||||
|
||||
# pylint: disable=unnecessary-pass, too-few-public-methods, too-many-instance-attributes
|
||||
class Detector(ABC):
|
||||
@abstractmethod
|
||||
def detect_faces(
|
||||
self,
|
||||
img: Union[np.ndarray, List[np.ndarray]]
|
||||
) -> Union[List["FacialAreaRegion"], List[List["FacialAreaRegion"]]]:
|
||||
"""
|
||||
Interface for detect and align faces in a batch of images
|
||||
|
||||
Args:
|
||||
img (Union[np.ndarray, List[np.ndarray]]):
|
||||
Pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (Union[List[List[FacialAreaRegion]], List[FacialAreaRegion]]):
|
||||
A list or a list of lists of FacialAreaRegion objects
|
||||
where each object contains:
|
||||
|
||||
- facial_area (FacialAreaRegion): The facial area region represented
|
||||
as x, y, w, h, left_eye and right_eye. left eye and right eye are
|
||||
eyes on the left and right respectively with respect to the person
|
||||
instead of observer.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# pylint: disable=unnecessary-pass, too-few-public-methods, too-many-instance-attributes
|
||||
@dataclass
|
||||
@ -49,56 +78,3 @@ class DetectedFace:
|
||||
img: np.ndarray
|
||||
facial_area: FacialAreaRegion
|
||||
confidence: float
|
||||
|
||||
|
||||
# Notice that all facial detector models must be inherited from this class
|
||||
|
||||
class Detector(ABC):
|
||||
|
||||
def detect_faces(
|
||||
self,
|
||||
img: Union[np.ndarray, List[np.ndarray]],
|
||||
) -> Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]:
|
||||
"""
|
||||
Detect and align faces in an image or a list of images
|
||||
|
||||
Args:
|
||||
img (Union[np.ndarray, List[np.ndarray]]):
|
||||
pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]):
|
||||
A list or a list of lists of FacialAreaRegion objects
|
||||
"""
|
||||
is_batched_input = isinstance(img, list)
|
||||
if not is_batched_input:
|
||||
img = [img]
|
||||
results = [self._process_single_image(single_img) for single_img in img]
|
||||
if not is_batched_input:
|
||||
return results[0]
|
||||
return results
|
||||
|
||||
def _process_single_image(
|
||||
self,
|
||||
img: np.ndarray
|
||||
) -> List[FacialAreaRegion]:
|
||||
"""
|
||||
Interface for detect and align faces in a single image
|
||||
|
||||
Args:
|
||||
img (Union[np.ndarray, List[np.ndarray]]):
|
||||
Pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (Union[List[List[FacialAreaRegion]], List[FacialAreaRegion]]):
|
||||
A list or a list of lists of FacialAreaRegion objects
|
||||
where each object contains:
|
||||
|
||||
- facial_area (FacialAreaRegion): The facial area region represented
|
||||
as x, y, w, h, left_eye and right_eye. left eye and right eye are
|
||||
eyes on the left and right respectively with respect to the person
|
||||
instead of observer.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"Subclasses that do not implement batch detection must implement this method"
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# built-in dependencies
|
||||
import os
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
|
||||
# 3rd party dependencies
|
||||
import numpy as np
|
||||
@ -34,12 +34,35 @@ class CenterFaceClient(Detector):
|
||||
|
||||
return CenterFace(weight_path=weights_path)
|
||||
|
||||
def _process_single_image(self, img: np.ndarray) -> List[FacialAreaRegion]:
|
||||
def detect_faces(
|
||||
self,
|
||||
img: Union[np.ndarray, List[np.ndarray]],
|
||||
) -> Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]:
|
||||
"""
|
||||
Detect and align face with CenterFace
|
||||
|
||||
Args:
|
||||
img (Union[np.ndarray, List[np.ndarray]]):
|
||||
pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]):
|
||||
A list or a list of lists of FacialAreaRegion objects
|
||||
"""
|
||||
is_batched_input = isinstance(img, list)
|
||||
if not is_batched_input:
|
||||
img = [img]
|
||||
results = [self._process_single_image(single_img) for single_img in img]
|
||||
if not is_batched_input:
|
||||
return results[0]
|
||||
return results
|
||||
|
||||
def _process_single_image(self, single_img: np.ndarray) -> List[FacialAreaRegion]:
|
||||
"""
|
||||
Helper function to detect faces in a single image.
|
||||
|
||||
Args:
|
||||
img (np.ndarray): pre-loaded image as numpy array
|
||||
single_img (np.ndarray): pre-loaded image as numpy array
|
||||
|
||||
Returns:
|
||||
results (List[FacialAreaRegion]): A list of FacialAreaRegion objects
|
||||
@ -53,7 +76,7 @@ class CenterFaceClient(Detector):
|
||||
# img, img.shape[0], img.shape[1], threshold=threshold
|
||||
# )
|
||||
detections, landmarks = self.build_model().forward(
|
||||
img, img.shape[0], img.shape[1], threshold=threshold
|
||||
single_img, single_img.shape[0], single_img.shape[1], threshold=threshold
|
||||
)
|
||||
|
||||
for i, detection in enumerate(detections):
|
||||
|
@ -1,5 +1,5 @@
|
||||
# built-in dependencies
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
|
||||
# 3rd party dependencies
|
||||
import numpy as np
|
||||
@ -47,6 +47,29 @@ class DlibClient(Detector):
|
||||
detector["sp"] = sp
|
||||
return detector
|
||||
|
||||
def detect_faces(
|
||||
self,
|
||||
img: Union[np.ndarray, List[np.ndarray]],
|
||||
) -> Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]:
|
||||
"""
|
||||
Detect and align face with dlib
|
||||
|
||||
Args:
|
||||
img (Union[np.ndarray, List[np.ndarray]]):
|
||||
pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]):
|
||||
A list or a list of lists of FacialAreaRegion objects
|
||||
"""
|
||||
is_batched_input = isinstance(img, list)
|
||||
if not is_batched_input:
|
||||
img = [img]
|
||||
results = [self._process_single_image(single_img) for single_img in img]
|
||||
if not is_batched_input:
|
||||
return results[0]
|
||||
return results
|
||||
|
||||
def _process_single_image(self, img: np.ndarray) -> List[FacialAreaRegion]:
|
||||
"""
|
||||
Helper function to detect faces in a single image.
|
||||
|
@ -17,6 +17,29 @@ class FastMtCnnClient(Detector):
|
||||
def __init__(self):
|
||||
self.model = self.build_model()
|
||||
|
||||
def detect_faces(
|
||||
self,
|
||||
img: Union[np.ndarray, List[np.ndarray]],
|
||||
) -> Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]:
|
||||
"""
|
||||
Detect and align face with mtcnn
|
||||
|
||||
Args:
|
||||
img (Union[np.ndarray, List[np.ndarray]]):
|
||||
pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]):
|
||||
A list or a list of lists of FacialAreaRegion objects
|
||||
"""
|
||||
is_batched_input = isinstance(img, list)
|
||||
if not is_batched_input:
|
||||
img = [img]
|
||||
results = [self._process_single_image(single_img) for single_img in img]
|
||||
if not is_batched_input:
|
||||
return results[0]
|
||||
return results
|
||||
|
||||
def _process_single_image(self, img: np.ndarray) -> List[FacialAreaRegion]:
|
||||
"""
|
||||
Helper function to detect faces in a single image.
|
||||
|
@ -1,6 +1,6 @@
|
||||
# built-in dependencies
|
||||
import os
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Union
|
||||
|
||||
# 3rd party dependencies
|
||||
import numpy as np
|
||||
@ -43,6 +43,29 @@ class MediaPipeClient(Detector):
|
||||
)
|
||||
return face_detection
|
||||
|
||||
def detect_faces(
|
||||
self,
|
||||
img: Union[np.ndarray, List[np.ndarray]],
|
||||
) -> Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]:
|
||||
"""
|
||||
Detect and align face with mediapipe
|
||||
|
||||
Args:
|
||||
img (Union[np.ndarray, List[np.ndarray]]):
|
||||
pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]):
|
||||
A list or a list of lists of FacialAreaRegion objects
|
||||
"""
|
||||
is_batched_input = isinstance(img, list)
|
||||
if not is_batched_input:
|
||||
img = [img]
|
||||
results = [self._process_single_image(single_img) for single_img in img]
|
||||
if not is_batched_input:
|
||||
return results[0]
|
||||
return results
|
||||
|
||||
def _process_single_image(self, img: np.ndarray) -> List[FacialAreaRegion]:
|
||||
"""
|
||||
Helper function to detect faces in a single image.
|
||||
|
@ -1,6 +1,6 @@
|
||||
# built-in dependencies
|
||||
import os
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Union
|
||||
import logging
|
||||
|
||||
# 3rd party dependencies
|
||||
@ -45,48 +45,65 @@ class OpenCvClient(Detector):
|
||||
)
|
||||
return supports_batch_detection
|
||||
|
||||
def _process_single_image(self, img: np.ndarray) -> List[FacialAreaRegion]:
|
||||
def detect_faces(
|
||||
self,
|
||||
img: Union[np.ndarray, List[np.ndarray]]
|
||||
) -> Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]:
|
||||
"""
|
||||
Helper function to detect faces in a single image.
|
||||
Detect and align face with opencv
|
||||
|
||||
Args:
|
||||
img (np.ndarray): pre-loaded image as numpy array
|
||||
img (Union[np.ndarray, List[np.ndarray]]):
|
||||
Pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (List[FacialAreaRegion]): A list of FacialAreaRegion objects
|
||||
results (Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]):
|
||||
A list or a list of lists of FacialAreaRegion objects
|
||||
"""
|
||||
resp = []
|
||||
detected_face = None
|
||||
faces = []
|
||||
try:
|
||||
faces, _, scores = self.model["face_detector"].detectMultiScale3(
|
||||
img, 1.1, 10, outputRejectLevels=True
|
||||
)
|
||||
except:
|
||||
pass
|
||||
if isinstance(img, np.ndarray):
|
||||
imgs = [img]
|
||||
elif self.supports_batch_detection:
|
||||
imgs = img
|
||||
else:
|
||||
return [self.detect_faces(single_img) for single_img in img]
|
||||
|
||||
if len(faces) > 0:
|
||||
for (x, y, w, h), confidence in zip(faces, scores):
|
||||
detected_face = img[int(y):int(y + h), int(x):int(x + w)]
|
||||
left_eye, right_eye = self.find_eyes(img=detected_face)
|
||||
batch_results = []
|
||||
|
||||
if left_eye is not None:
|
||||
left_eye = (int(x + left_eye[0]), int(y + left_eye[1]))
|
||||
if right_eye is not None:
|
||||
right_eye = (int(x + right_eye[0]), int(y + right_eye[1]))
|
||||
|
||||
facial_area = FacialAreaRegion(
|
||||
x=x,
|
||||
y=y,
|
||||
w=w,
|
||||
h=h,
|
||||
left_eye=left_eye,
|
||||
right_eye=right_eye,
|
||||
confidence=(100 - confidence) / 100,
|
||||
for single_img in imgs:
|
||||
resp = []
|
||||
detected_face = None
|
||||
faces = []
|
||||
try:
|
||||
faces, _, scores = self.model["face_detector"].detectMultiScale3(
|
||||
single_img, 1.1, 10, outputRejectLevels=True
|
||||
)
|
||||
resp.append(facial_area)
|
||||
except:
|
||||
pass
|
||||
|
||||
return resp
|
||||
if len(faces) > 0:
|
||||
for (x, y, w, h), confidence in zip(faces, scores):
|
||||
detected_face = single_img[int(y):int(y + h), int(x):int(x + w)]
|
||||
left_eye, right_eye = self.find_eyes(img=detected_face)
|
||||
|
||||
if left_eye is not None:
|
||||
left_eye = (int(x + left_eye[0]), int(y + left_eye[1]))
|
||||
if right_eye is not None:
|
||||
right_eye = (int(x + right_eye[0]), int(y + right_eye[1]))
|
||||
|
||||
facial_area = FacialAreaRegion(
|
||||
x=x,
|
||||
y=y,
|
||||
w=w,
|
||||
h=h,
|
||||
left_eye=left_eye,
|
||||
right_eye=right_eye,
|
||||
confidence=(100 - confidence) / 100,
|
||||
)
|
||||
resp.append(facial_area)
|
||||
|
||||
batch_results.append(resp)
|
||||
|
||||
return batch_results if len(batch_results) > 1 else batch_results[0]
|
||||
|
||||
def find_eyes(self, img: np.ndarray) -> tuple:
|
||||
"""
|
||||
|
@ -1,5 +1,5 @@
|
||||
# built-in dependencies
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
from enum import IntEnum
|
||||
|
||||
# 3rd party dependencies
|
||||
@ -54,25 +54,48 @@ class SsdClient(Detector):
|
||||
|
||||
return {"face_detector": face_detector, "opencv_module": OpenCv.OpenCvClient()}
|
||||
|
||||
def _process_single_image(self, img: np.ndarray) -> List[FacialAreaRegion]:
|
||||
def detect_faces(
|
||||
self,
|
||||
img: Union[np.ndarray, List[np.ndarray]]
|
||||
) -> Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]:
|
||||
"""
|
||||
Detect and align faces with ssd in a batch of images
|
||||
|
||||
Args:
|
||||
img (Union[np.ndarray, List[np.ndarray]]):
|
||||
Pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]):
|
||||
A list or a list of lists of FacialAreaRegion objects
|
||||
"""
|
||||
is_batched_input = isinstance(img, list)
|
||||
if not is_batched_input:
|
||||
img = [img]
|
||||
results = [self._process_single_image(single_img) for single_img in img]
|
||||
if not is_batched_input:
|
||||
return results[0]
|
||||
return results
|
||||
|
||||
def _process_single_image(self, single_img: np.ndarray) -> List[FacialAreaRegion]:
|
||||
"""
|
||||
Helper function to detect faces in a single image.
|
||||
|
||||
Args:
|
||||
img (np.ndarray): Pre-loaded image as numpy array
|
||||
single_img (np.ndarray): Pre-loaded image as numpy array
|
||||
|
||||
Returns:
|
||||
results (List[FacialAreaRegion]): A list of FacialAreaRegion objects
|
||||
"""
|
||||
# Because cv2.dnn.blobFromImage expects CV_8U (8-bit unsigned integer) values
|
||||
if img.dtype != np.uint8:
|
||||
img = img.astype(np.uint8)
|
||||
if single_img.dtype != np.uint8:
|
||||
single_img = single_img.astype(np.uint8)
|
||||
|
||||
opencv_module: OpenCv.OpenCvClient = self.model["opencv_module"]
|
||||
|
||||
target_size = (300, 300)
|
||||
original_size = img.shape
|
||||
current_img = cv2.resize(img, target_size)
|
||||
original_size = single_img.shape
|
||||
current_img = cv2.resize(single_img, target_size)
|
||||
|
||||
aspect_ratio_x = original_size[1] / target_size[1]
|
||||
aspect_ratio_y = original_size[0] / target_size[0]
|
||||
@ -109,7 +132,7 @@ class SsdClient(Detector):
|
||||
for face in faces:
|
||||
confidence = float(face[ssd_labels.confidence])
|
||||
x, y, w, h = map(int, face[margins])
|
||||
detected_face = img[y : y + h, x : x + w]
|
||||
detected_face = single_img[y : y + h, x : x + w]
|
||||
|
||||
left_eye, right_eye = opencv_module.find_eyes(detected_face)
|
||||
|
||||
|
@ -78,8 +78,7 @@ class YoloDetectorClient(Detector):
|
||||
A list of lists of FacialAreaRegion objects
|
||||
for each image or a list of FacialAreaRegion objects
|
||||
"""
|
||||
is_batched_input = isinstance(img, list)
|
||||
if not is_batched_input:
|
||||
if not isinstance(img, list):
|
||||
img = [img]
|
||||
|
||||
all_results = []
|
||||
@ -143,7 +142,7 @@ class YoloDetectorClient(Detector):
|
||||
|
||||
all_results.append(resp)
|
||||
|
||||
if not is_batched_input:
|
||||
if len(all_results) == 1:
|
||||
return all_results[0]
|
||||
return all_results
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# built-in dependencies
|
||||
import os
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Union
|
||||
|
||||
# 3rd party dependencies
|
||||
import cv2
|
||||
@ -57,6 +57,24 @@ class YuNetClient(Detector):
|
||||
) from err
|
||||
return face_detector
|
||||
|
||||
def detect_faces(self, img: Union[np.ndarray, List[np.ndarray]]) -> Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]:
|
||||
"""
|
||||
Detect and align face with yunet
|
||||
|
||||
Args:
|
||||
img (Union[np.ndarray, List[np.ndarray]]): pre-loaded image as numpy array or a list of those
|
||||
|
||||
Returns:
|
||||
results (Union[List[FacialAreaRegion], List[List[FacialAreaRegion]]]): A list or a list of lists of FacialAreaRegion objects
|
||||
"""
|
||||
is_batched_input = isinstance(img, list)
|
||||
if not is_batched_input:
|
||||
img = [img]
|
||||
results = [self._process_single_image(single_img) for single_img in img]
|
||||
if not is_batched_input:
|
||||
return results[0]
|
||||
return results
|
||||
|
||||
def _process_single_image(self, img: np.ndarray) -> List[FacialAreaRegion]:
|
||||
"""
|
||||
Helper function to detect faces in a single image.
|
||||
|
Loading…
x
Reference in New Issue
Block a user