From 359162aa5e9993e6b8bfe9008b0e8a079eea9271 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Sun, 21 Jan 2024 18:10:21 +0000 Subject: [PATCH] details about return types --- deepface/basemodels/ArcFace.py | 3 +- deepface/basemodels/DeepID.py | 3 +- deepface/basemodels/Dlib.py | 3 +- deepface/basemodels/Facenet.py | 5 +-- deepface/basemodels/FbDeepFace.py | 3 +- deepface/basemodels/OpenFace.py | 3 +- deepface/basemodels/SFace.py | 4 +-- deepface/basemodels/VGGFace.py | 3 +- deepface/commons/functions.py | 17 +++++---- deepface/detectors/DetectorWrapper.py | 17 +++++++-- deepface/detectors/Dlib.py | 19 ++++++++-- deepface/detectors/FastMtCnn.py | 33 ++++++++++++----- deepface/detectors/MediaPipe.py | 31 +++++++++++----- deepface/detectors/MtCnn.py | 32 ++++++++++++----- deepface/detectors/OpenCv.py | 23 +++++++++--- deepface/detectors/RetinaFace.py | 21 +++++++++-- deepface/detectors/Ssd.py | 24 ++++++++++--- deepface/detectors/Yolo.py | 23 +++++++++--- deepface/detectors/YuNet.py | 25 ++++++++++--- deepface/models/Detector.py | 52 ++++++++++++--------------- deepface/models/FacialRecognition.py | 4 +-- deepface/modules/detection.py | 36 +++++++++++++++++-- 22 files changed, 284 insertions(+), 100 deletions(-) diff --git a/deepface/basemodels/ArcFace.py b/deepface/basemodels/ArcFace.py index 2a59636..bb639f3 100644 --- a/deepface/basemodels/ArcFace.py +++ b/deepface/basemodels/ArcFace.py @@ -1,3 +1,4 @@ +from typing import List import os import gdown import numpy as np @@ -53,7 +54,7 @@ class ArcFaceClient(FacialRecognition): self.model = load_model() self.model_name = "ArcFace" - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: """ find embeddings with ArcFace model Args: diff --git a/deepface/basemodels/DeepID.py b/deepface/basemodels/DeepID.py index 71b10b1..8b4a7d5 100644 --- a/deepface/basemodels/DeepID.py +++ b/deepface/basemodels/DeepID.py @@ -1,3 +1,4 @@ +from typing import List import os import gdown import numpy as np @@ -49,7 +50,7 @@ class DeepIdClient(FacialRecognition): self.model = load_model() self.model_name = "DeepId" - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: """ find embeddings with DeepId model Args: diff --git a/deepface/basemodels/Dlib.py b/deepface/basemodels/Dlib.py index 6ccfec8..6d06ef4 100644 --- a/deepface/basemodels/Dlib.py +++ b/deepface/basemodels/Dlib.py @@ -1,3 +1,4 @@ +from typing import List import os import bz2 import gdown @@ -20,7 +21,7 @@ class DlibClient(FacialRecognition): self.model = DlibResNet() self.model_name = "Dlib" - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: """ find embeddings with Dlib model - different than regular models Args: diff --git a/deepface/basemodels/Facenet.py b/deepface/basemodels/Facenet.py index 1c3dac8..d95da02 100644 --- a/deepface/basemodels/Facenet.py +++ b/deepface/basemodels/Facenet.py @@ -1,3 +1,4 @@ +from typing import List import os import gdown import numpy as np @@ -53,7 +54,7 @@ class FaceNet128dClient(FacialRecognition): self.model = load_facenet128d_model() self.model_name = "FaceNet-128d" - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: """ find embeddings with FaceNet-128d model Args: @@ -75,7 +76,7 @@ class FaceNet512dClient(FacialRecognition): self.model = load_facenet512d_model() self.model_name = "FaceNet-512d" - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: """ find embeddings with FaceNet-512d model Args: diff --git a/deepface/basemodels/FbDeepFace.py b/deepface/basemodels/FbDeepFace.py index 30a8aec..075626b 100644 --- a/deepface/basemodels/FbDeepFace.py +++ b/deepface/basemodels/FbDeepFace.py @@ -1,3 +1,4 @@ +from typing import List import os import zipfile import gdown @@ -46,7 +47,7 @@ class DeepFaceClient(FacialRecognition): self.model = load_model() self.model_name = "DeepFace" - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: """ find embeddings with OpenFace model Args: diff --git a/deepface/basemodels/OpenFace.py b/deepface/basemodels/OpenFace.py index 1ba4d1c..3867291 100644 --- a/deepface/basemodels/OpenFace.py +++ b/deepface/basemodels/OpenFace.py @@ -1,3 +1,4 @@ +from typing import List import os import gdown import tensorflow as tf @@ -36,7 +37,7 @@ class OpenFaceClient(FacialRecognition): self.model = load_model() self.model_name = "OpenFace" - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: """ find embeddings with OpenFace model Args: diff --git a/deepface/basemodels/SFace.py b/deepface/basemodels/SFace.py index 2a59ccb..ba8b55e 100644 --- a/deepface/basemodels/SFace.py +++ b/deepface/basemodels/SFace.py @@ -1,5 +1,5 @@ import os -from typing import Any +from typing import Any, List import numpy as np import cv2 as cv @@ -23,7 +23,7 @@ class SFaceClient(FacialRecognition): self.model = load_model() self.model_name = "SFace" - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: """ find embeddings with SFace model - different than regular models Args: diff --git a/deepface/basemodels/VGGFace.py b/deepface/basemodels/VGGFace.py index fa548a1..80d6d87 100644 --- a/deepface/basemodels/VGGFace.py +++ b/deepface/basemodels/VGGFace.py @@ -1,3 +1,4 @@ +from typing import List import os import gdown import numpy as np @@ -47,7 +48,7 @@ class VggFaceClient(FacialRecognition): self.model = load_model() self.model_name = "VGG-Face" - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: """ find embeddings with VGG-Face model Args: diff --git a/deepface/commons/functions.py b/deepface/commons/functions.py index 22b76f1..dc2a5b2 100644 --- a/deepface/commons/functions.py +++ b/deepface/commons/functions.py @@ -1,5 +1,5 @@ import os -from typing import Union, Tuple +from typing import Union, Tuple, List import base64 from pathlib import Path @@ -140,9 +140,9 @@ def extract_faces( grayscale: bool = False, enforce_detection: bool = True, align: bool = True, -) -> list: - """Extract faces from an image. - +) -> List[Tuple[np.ndarray, dict, float]]: + """ + Extract faces from an image. Args: img: a path, url, base64 or numpy array. target_size (tuple, optional): the target size of the extracted faces. @@ -157,7 +157,12 @@ def extract_faces( ValueError: if face could not be detected and enforce_detection is True. Returns: - list: a list of extracted faces. + results (List[Tuple[np.ndarray, dict, float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (dict): The image region represented as + {"x": x, "y": y, "w": w, "h": h} + - confidence (float): The confidence score associated with the detected face. """ # this is going to store a list of img itself (numpy), it region and confidence @@ -246,7 +251,7 @@ def extract_faces( "h": int(current_region[3]), } - extracted_face = [img_pixels, region_obj, confidence] + extracted_face = (img_pixels, region_obj, confidence) extracted_faces.append(extracted_face) if len(extracted_faces) == 0 and enforce_detection == True: diff --git a/deepface/detectors/DetectorWrapper.py b/deepface/detectors/DetectorWrapper.py index 8c3ba91..d061d79 100644 --- a/deepface/detectors/DetectorWrapper.py +++ b/deepface/detectors/DetectorWrapper.py @@ -59,9 +59,20 @@ def detect_faces(detector_backend: str, img: np.ndarray, align: bool = True) -> detector_backend (str): detector name img (np.ndarray): pre-loaded image alig (bool): enable or disable alignment after detection - Returns - result (list): tuple of face (np.ndarray), face region (list) - , confidence score (float) + Returns: + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ face_detector: Detector = build_model(detector_backend) return face_detector.detect_faces(img=img, align=align) diff --git a/deepface/detectors/Dlib.py b/deepface/detectors/Dlib.py index 9d2bcfd..fead85e 100644 --- a/deepface/detectors/Dlib.py +++ b/deepface/detectors/Dlib.py @@ -1,3 +1,4 @@ +from typing import List, Tuple import os import bz2 import gdown @@ -55,7 +56,9 @@ class DlibClient(Detector): detector["sp"] = sp return detector - def detect_faces(self, img: np.ndarray, align: bool = True) -> list: + def detect_faces( + self, img: np.ndarray, align: bool = True + ) -> List[Tuple[np.ndarray, List[float], float]]: """ Detect and align face with dlib Args: @@ -63,7 +66,19 @@ class DlibClient(Detector): img (np.ndarray): pre-loaded image align (bool): default is true Returns: - list of detected and aligned faces + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ # this is not a must dependency. do not import it in the global level. try: diff --git a/deepface/detectors/FastMtCnn.py b/deepface/detectors/FastMtCnn.py index 2f037d5..c43ed55 100644 --- a/deepface/detectors/FastMtCnn.py +++ b/deepface/detectors/FastMtCnn.py @@ -1,7 +1,8 @@ -from typing import Any, Union +from typing import Any, Union, List, Tuple import cv2 import numpy as np from deepface.models.Detector import Detector +from deepface.modules import detection # Link -> https://github.com/timesler/facenet-pytorch # Examples https://www.kaggle.com/timesler/guide-to-mtcnn-in-facenet-pytorch @@ -11,14 +12,28 @@ class FastMtCnnClient(Detector): def __init__(self): self.model = self.build_model() - def detect_faces(self, img: np.ndarray, align: bool = True) -> list: + def detect_faces( + self, img: np.ndarray, align: bool = True + ) -> List[Tuple[np.ndarray, List[float], float]]: """ Detect and align face with mtcnn Args: img (np.ndarray): pre-loaded image align (bool): default is true Returns: - list of detected and aligned faces + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ resp = [] @@ -31,16 +46,16 @@ class FastMtCnnClient(Detector): ) # returns boundingbox, prob, landmark if len(detections[0]) > 0: - for detection in zip(*detections): - x, y, w, h = xyxy_to_xywh(detection[0]) + for current_detection in zip(*detections): + x, y, w, h = xyxy_to_xywh(current_detection[0]) detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] img_region = [x, y, w, h] - confidence = detection[1] + confidence = current_detection[1] if align: - left_eye = detection[2][0] - right_eye = detection[2][1] - detected_face = self.align_face( + left_eye = current_detection[2][0] + right_eye = current_detection[2][1] + detected_face = detection.align_face( img=detected_face, left_eye=left_eye, right_eye=right_eye ) diff --git a/deepface/detectors/MediaPipe.py b/deepface/detectors/MediaPipe.py index 73a2fe2..0e067a2 100644 --- a/deepface/detectors/MediaPipe.py +++ b/deepface/detectors/MediaPipe.py @@ -1,6 +1,7 @@ -from typing import Any +from typing import Any, List, Tuple import numpy as np from deepface.models.Detector import Detector +from deepface.modules import detection # Link - https://google.github.io/mediapipe/solutions/face_detection @@ -28,14 +29,28 @@ class MediaPipeClient(Detector): face_detection = mp_face_detection.FaceDetection(min_detection_confidence=0.7) return face_detection - def detect_faces(self, img: np.ndarray, align: bool = True) -> list: + def detect_faces( + self, img: np.ndarray, align: bool = True + ) -> List[Tuple[np.ndarray, List[float], float]]: """ Detect and align face with mediapipe Args: img (np.ndarray): pre-loaded image align (bool): default is true Returns: - list of detected and aligned faces + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ resp = [] @@ -49,11 +64,11 @@ class MediaPipeClient(Detector): return resp # Extract the bounding box, the landmarks and the confidence score - for detection in results.detections: - (confidence,) = detection.score + for current_detection in results.detections: + (confidence,) = current_detection.score - bounding_box = detection.location_data.relative_bounding_box - landmarks = detection.location_data.relative_keypoints + bounding_box = current_detection.location_data.relative_bounding_box + landmarks = current_detection.location_data.relative_keypoints x = int(bounding_box.xmin * img_width) w = int(bounding_box.width * img_width) @@ -73,7 +88,7 @@ class MediaPipeClient(Detector): img_region = [x, y, w, h] if align: - detected_face = self.align_face( + detected_face = detection.align_face( img=detected_face, left_eye=left_eye, right_eye=right_eye ) diff --git a/deepface/detectors/MtCnn.py b/deepface/detectors/MtCnn.py index 8f581ad..aefb5f6 100644 --- a/deepface/detectors/MtCnn.py +++ b/deepface/detectors/MtCnn.py @@ -1,9 +1,11 @@ +from typing import List, Tuple import cv2 import numpy as np from mtcnn import MTCNN from deepface.models.Detector import Detector +from deepface.modules import detection - +# pylint: disable=too-few-public-methods class MtCnnClient(Detector): """ Class to cover common face detection functionalitiy for MtCnn backend @@ -12,14 +14,28 @@ class MtCnnClient(Detector): def __init__(self): self.model = MTCNN() - def detect_faces(self, img: np.ndarray, align: bool = True) -> list: + def detect_faces( + self, img: np.ndarray, align: bool = True + ) -> List[Tuple[np.ndarray, List[float], float]]: """ Detect and align face with mtcnn Args: img (np.ndarray): pre-loaded image align (bool): default is true Returns: - list of detected and aligned faces + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ resp = [] @@ -32,17 +48,17 @@ class MtCnnClient(Detector): if len(detections) > 0: - for detection in detections: - x, y, w, h = detection["box"] + for current_detection in detections: + x, y, w, h = current_detection["box"] detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] img_region = [x, y, w, h] - confidence = detection["confidence"] + confidence = current_detection["confidence"] if align: - keypoints = detection["keypoints"] + keypoints = current_detection["keypoints"] left_eye = keypoints["left_eye"] right_eye = keypoints["right_eye"] - detected_face = self.align_face( + detected_face = detection.align_face( img=detected_face, left_eye=left_eye, right_eye=right_eye ) diff --git a/deepface/detectors/OpenCv.py b/deepface/detectors/OpenCv.py index e70d324..6f1d21c 100644 --- a/deepface/detectors/OpenCv.py +++ b/deepface/detectors/OpenCv.py @@ -1,8 +1,9 @@ import os -from typing import Any +from typing import Any, List, Tuple import cv2 import numpy as np from deepface.models.Detector import Detector +from deepface.modules import detection class OpenCvClient(Detector): @@ -24,7 +25,9 @@ class OpenCvClient(Detector): detector["eye_detector"] = self.__build_cascade("haarcascade_eye") return detector - def detect_faces(self, img: np.ndarray, align: bool = True) -> list: + def detect_faces( + self, img: np.ndarray, align: bool = True + ) -> List[Tuple[np.ndarray, List[float], float]]: """ Detect and align face with opencv Args: @@ -32,7 +35,19 @@ class OpenCvClient(Detector): img (np.ndarray): pre-loaded image align (bool): default is true Returns: - list of detected and aligned faces + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ resp = [] @@ -56,7 +71,7 @@ class OpenCvClient(Detector): if align: left_eye, right_eye = self.find_eyes(img=detected_face) - detected_face = self.align_face(detected_face, left_eye, right_eye) + detected_face = detection.align_face(detected_face, left_eye, right_eye) img_region = [x, y, w, h] diff --git a/deepface/detectors/RetinaFace.py b/deepface/detectors/RetinaFace.py index 3d2d9ae..0eb8bf1 100644 --- a/deepface/detectors/RetinaFace.py +++ b/deepface/detectors/RetinaFace.py @@ -1,21 +1,36 @@ +from typing import List, Tuple import numpy as np from retinaface import RetinaFace as rf from retinaface.commons import postprocess from deepface.models.Detector import Detector - +# pylint: disable=too-few-public-methods class RetinaFaceClient(Detector): def __init__(self): self.model = rf.build_model() - def detect_faces(self, img: np.ndarray, align: bool = True) -> list: + def detect_faces( + self, img: np.ndarray, align: bool = True + ) -> List[Tuple[np.ndarray, List[float], float]]: """ Detect and align face with retinaface Args: img (np.ndarray): pre-loaded image align (bool): default is true Returns: - list of detected and aligned faces + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ resp = [] diff --git a/deepface/detectors/Ssd.py b/deepface/detectors/Ssd.py index 7847e12..50bc4cd 100644 --- a/deepface/detectors/Ssd.py +++ b/deepface/detectors/Ssd.py @@ -1,3 +1,4 @@ +from typing import List, Tuple import os import gdown import cv2 @@ -6,6 +7,7 @@ import numpy as np from deepface.detectors import OpenCv from deepface.commons import functions from deepface.models.Detector import Detector +from deepface.modules import detection from deepface.commons.logger import Logger logger = Logger(module="detectors.SsdWrapper") @@ -69,14 +71,28 @@ class SsdClient(Detector): return detector - def detect_faces(self, img: np.ndarray, align: bool = True) -> list: + def detect_faces( + self, img: np.ndarray, align: bool = True + ) -> List[Tuple[np.ndarray, List[float], float]]: """ Detect and align face with ssd Args: img (np.ndarray): pre-loaded image align (bool): default is true Returns: - list of detected and aligned faces + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ resp = [] @@ -134,9 +150,9 @@ class SsdClient(Detector): confidence = instance["confidence"] if align: - opencv_module: OpenCv.OpenCv = self.model["opencv_module"] + opencv_module: OpenCv.OpenCvClient = self.model["opencv_module"] left_eye, right_eye = opencv_module.find_eyes(detected_face) - detected_face = self.align_face( + detected_face = detection.align_face( img=detected_face, left_eye=left_eye, right_eye=right_eye ) diff --git a/deepface/detectors/Yolo.py b/deepface/detectors/Yolo.py index 5a97db6..44bd4bb 100644 --- a/deepface/detectors/Yolo.py +++ b/deepface/detectors/Yolo.py @@ -1,6 +1,7 @@ -from typing import Any +from typing import Any, List, Tuple import numpy as np from deepface.models.Detector import Detector +from deepface.modules import detection from deepface.commons.logger import Logger logger = Logger() @@ -50,7 +51,9 @@ class YoloClient(Detector): # Return face_detector return YOLO(weight_path) - def detect_faces(self, img: np.ndarray, align: bool = False) -> list: + def detect_faces( + self, img: np.ndarray, align: bool = False + ) -> List[Tuple[np.ndarray, List[float], float]]: """ Detect and align face with yolo Args: @@ -58,7 +61,19 @@ class YoloClient(Detector): img (np.ndarray): pre-loaded image align (bool): default is true Returns: - list of detected and aligned faces + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ resp = [] @@ -85,7 +100,7 @@ class YoloClient(Detector): left_eye[1] > LANDMARKS_CONFIDENCE_THRESHOLD and right_eye[1] > LANDMARKS_CONFIDENCE_THRESHOLD ): - detected_face = self.align_face( + detected_face = detection.align_face( img=detected_face, left_eye=left_eye[0].cpu(), right_eye=right_eye[0].cpu() ) resp.append((detected_face, [x, y, w, h], confidence)) diff --git a/deepface/detectors/YuNet.py b/deepface/detectors/YuNet.py index 41c1764..2f7e973 100644 --- a/deepface/detectors/YuNet.py +++ b/deepface/detectors/YuNet.py @@ -1,11 +1,12 @@ import os -from typing import Any +from typing import Any, List, Tuple import cv2 import numpy as np import gdown from deepface.commons import functions -from deepface.commons.logger import Logger from deepface.models.Detector import Detector +from deepface.modules import detection +from deepface.commons.logger import Logger logger = Logger(module="detectors.YunetWrapper") @@ -41,14 +42,28 @@ class YuNetClient(Detector): ) from err return face_detector - def detect_faces(self, img: np.ndarray, align: bool = True) -> list: + def detect_faces( + self, img: np.ndarray, align: bool = True + ) -> List[Tuple[np.ndarray, List[float], float]]: """ Detect and align face with yunet Args: img (np.ndarray): pre-loaded image align (bool): default is true Returns: - list of detected and aligned faces + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ # FaceDetector.detect_faces does not support score_threshold parameter. # We can set it via environment variable. @@ -107,6 +122,6 @@ class YuNetClient(Detector): detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] img_region = [x, y, w, h] if align: - detected_face = self.align_face(detected_face, (x_re, y_re), (x_le, y_le)) + detected_face = detection.align_face(detected_face, (x_re, y_re), (x_le, y_le)) resp.append((detected_face, img_region, confidence)) return resp diff --git a/deepface/models/Detector.py b/deepface/models/Detector.py index 60bf114..b834e9d 100644 --- a/deepface/models/Detector.py +++ b/deepface/models/Detector.py @@ -1,42 +1,34 @@ +from typing import List, Tuple from abc import ABC, abstractmethod -from typing import Union, Optional import numpy as np -from PIL import Image # Notice that all facial detector models must be inherited from this class +# pylint: disable=unnecessary-pass, too-few-public-methods class Detector(ABC): @abstractmethod - def detect_faces(self, img: np.ndarray, align: bool = True) -> list: - pass - - def align_face( - self, - img: np.ndarray, - left_eye: Optional[Union[list, tuple]] = None, - right_eye: Optional[Union[list, tuple]] = None, - ) -> np.ndarray: + def detect_faces( + self, img: np.ndarray, align: bool = True + ) -> List[Tuple[np.ndarray, List[float], float]]: """ - Align a given image horizantally with respect to their left and right eye locations + Detect faces from a given image Args: - img (np.ndarray): pre-loaded image with detected face - left_eye (list or tuple): coordinates of left eye with respect to the you - right_eye(list or tuple): coordinates of right eye with respect to the you + img (np.ndarray): pre-loaded image as a NumPy array + align (bool): enable or disable alignment after face detection Returns: - img (np.ndarray): aligned facial image + results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples + where each tuple contains: + - detected_face (np.ndarray): The detected face as a NumPy array. + - face_region (List[float]): The image region represented as + a list of floats e.g. [x, y, w, h] + - confidence (float): The confidence score associated with the detected face. + + Example: + results = [ + (array(..., dtype=uint8), [110, 60, 150, 380], 0.99), + (array(..., dtype=uint8), [150, 50, 299, 375], 0.98), + (array(..., dtype=uint8), [120, 55, 300, 371], 0.96), + ] """ - # if eye could not be detected for the given image, return image itself - if left_eye is None or right_eye is None: - return img - - # sometimes unexpectedly detected images come with nil dimensions - if img.shape[0] == 0 or img.shape[1] == 0: - return img - - angle = float( - np.degrees(np.arctan2(right_eye[1] - left_eye[1], right_eye[0] - left_eye[0])) - ) - img = Image.fromarray(img) - img = np.array(img.rotate(angle)) - return img + pass diff --git a/deepface/models/FacialRecognition.py b/deepface/models/FacialRecognition.py index 7f323f4..b49292c 100644 --- a/deepface/models/FacialRecognition.py +++ b/deepface/models/FacialRecognition.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Any, Union +from typing import Any, Union, List import numpy as np from deepface.commons import functions @@ -17,5 +17,5 @@ class FacialRecognition(ABC): model_name: str @abstractmethod - def find_embeddings(self, img: np.ndarray) -> list: + def find_embeddings(self, img: np.ndarray) -> List[float]: pass diff --git a/deepface/modules/detection.py b/deepface/modules/detection.py index ae92aaa..1973c56 100644 --- a/deepface/modules/detection.py +++ b/deepface/modules/detection.py @@ -3,6 +3,7 @@ from typing import Any, Dict, List, Tuple, Union # 3rd part dependencies import numpy as np +from PIL import Image # project dependencies from deepface.commons import functions @@ -40,8 +41,11 @@ def extract_faces( grayscale (boolean): extracting faces in rgb or gray scale Returns: - list of dictionaries. Each dictionary will have facial image itself (RGB), - extracted area from the original image and confidence score. + results (List[Dict[str, Any]]): A list of dictionaries, where each dictionary contains: + - "face" (np.ndarray): The detected face as a NumPy array. + - "facial_area" (List[float]): The detected face's regions represented as a list of floats. + - "confidence" (float): The confidence score associated with the detected face. + """ @@ -70,3 +74,31 @@ def extract_faces( resp_objs.append(resp_obj) return resp_objs + + +def align_face( + img: np.ndarray, + left_eye: Union[list, tuple], + right_eye: Union[list, tuple], +) -> np.ndarray: + """ + Align a given image horizantally with respect to their left and right eye locations + Args: + img (np.ndarray): pre-loaded image with detected face + left_eye (list or tuple): coordinates of left eye with respect to the you + right_eye(list or tuple): coordinates of right eye with respect to the you + Returns: + img (np.ndarray): aligned facial image + """ + # if eye could not be detected for the given image, return image itself + if left_eye is None or right_eye is None: + return img + + # sometimes unexpectedly detected images come with nil dimensions + if img.shape[0] == 0 or img.shape[1] == 0: + return img + + angle = float(np.degrees(np.arctan2(right_eye[1] - left_eye[1], right_eye[0] - left_eye[0]))) + img = Image.fromarray(img) + img = np.array(img.rotate(angle)) + return img