Merge pull request #978 from serengil/feat-task-2701-return-obj-for-detectors

pretty return type for detect faces
This commit is contained in:
Sefik Ilkin Serengil 2024-01-27 19:30:28 +00:00 committed by GitHub
commit 35025cdf6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 193 additions and 235 deletions

View File

@ -12,6 +12,7 @@ import tensorflow as tf
# package dependencies # package dependencies
from deepface.detectors import DetectorWrapper from deepface.detectors import DetectorWrapper
from deepface.models.Detector import DetectedFace, FacialAreaRegion
from deepface.commons.logger import Logger from deepface.commons.logger import Logger
logger = Logger(module="commons.functions") logger = Logger(module="commons.functions")
@ -170,10 +171,11 @@ def extract_faces(
# img might be path, base64 or numpy array. Convert it to numpy whatever it is. # img might be path, base64 or numpy array. Convert it to numpy whatever it is.
img, img_name = load_image(img) img, img_name = load_image(img)
img_region = [0, 0, img.shape[1], img.shape[0]]
base_region = FacialAreaRegion(x=0, y=0, w=img.shape[1], h=img.shape[0])
if detector_backend == "skip": if detector_backend == "skip":
face_objs = [(img, img_region, 0)] face_objs = [DetectedFace(img=img, facial_area=base_region, confidence=0)]
else: else:
face_objs = DetectorWrapper.detect_faces(detector_backend, img, align) face_objs = DetectorWrapper.detect_faces(detector_backend, img, align)
@ -192,9 +194,12 @@ def extract_faces(
) )
if len(face_objs) == 0 and enforce_detection is False: if len(face_objs) == 0 and enforce_detection is False:
face_objs = [(img, img_region, 0)] face_objs = [DetectedFace(img=img, facial_area=base_region, confidence=0)]
for current_img, current_region, confidence in face_objs: for face_obj in face_objs:
current_img = face_obj.img
current_region = face_obj.facial_area
confidence = face_obj.confidence
if current_img.shape[0] > 0 and current_img.shape[1] > 0: if current_img.shape[0] > 0 and current_img.shape[1] > 0:
if grayscale is True: if grayscale is True:
current_img = cv2.cvtColor(current_img, cv2.COLOR_BGR2GRAY) current_img = cv2.cvtColor(current_img, cv2.COLOR_BGR2GRAY)
@ -245,10 +250,10 @@ def extract_faces(
# int cast is for the exception - object of type 'float32' is not JSON serializable # int cast is for the exception - object of type 'float32' is not JSON serializable
region_obj = { region_obj = {
"x": int(current_region[0]), "x": current_region.x,
"y": int(current_region[1]), "y": current_region.y,
"w": int(current_region[2]), "w": current_region.w,
"h": int(current_region[3]), "h": current_region.h,
} }
extracted_face = (img_pixels, region_obj, confidence) extracted_face = (img_pixels, region_obj, confidence)

View File

@ -1,6 +1,6 @@
from typing import Any from typing import Any, List
import numpy as np import numpy as np
from deepface.models.Detector import Detector from deepface.models.Detector import Detector, DetectedFace
from deepface.detectors import ( from deepface.detectors import (
FastMtCnn, FastMtCnn,
MediaPipe, MediaPipe,
@ -52,7 +52,7 @@ def build_model(detector_backend: str) -> Any:
return face_detector_obj[detector_backend] return face_detector_obj[detector_backend]
def detect_faces(detector_backend: str, img: np.ndarray, align: bool = True) -> list: def detect_faces(detector_backend: str, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
""" """
Detect face(s) from a given image Detect face(s) from a given image
Args: Args:
@ -60,19 +60,11 @@ def detect_faces(detector_backend: str, img: np.ndarray, align: bool = True) ->
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
alig (bool): enable or disable alignment after detection alig (bool): enable or disable alignment after detection
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[DetectedFace]): A list of DetectedFace objects
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h]
- confidence (float): The confidence score associated with the detected face. - 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) face_detector: Detector = build_model(detector_backend)
return face_detector.detect_faces(img=img, align=align) return face_detector.detect_faces(img=img, align=align)

View File

@ -1,10 +1,10 @@
from typing import List, Tuple from typing import List
import os import os
import bz2 import bz2
import gdown import gdown
import numpy as np import numpy as np
from deepface.commons import functions from deepface.commons import functions
from deepface.models.Detector import Detector from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.commons.logger import Logger from deepface.commons.logger import Logger
logger = Logger(module="detectors.DlibWrapper") logger = Logger(module="detectors.DlibWrapper")
@ -56,9 +56,7 @@ class DlibClient(Detector):
detector["sp"] = sp detector["sp"] = sp
return detector return detector
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect and align face with dlib Detect and align face with dlib
Args: Args:
@ -66,19 +64,11 @@ class DlibClient(Detector):
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): default is true
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[DetectedFace]): A list of DetectedFace objects
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h] - confidence (float): The confidence score associated with the detected face.
- 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. # this is not a must dependency. do not import it in the global level.
try: try:
@ -95,8 +85,6 @@ class DlibClient(Detector):
detected_face = None detected_face = None
img_region = [0, 0, img.shape[1], img.shape[0]]
face_detector = self.model["face_detector"] face_detector = self.model["face_detector"]
# note that, by design, dlib's fhog face detector scores are >0 but not capped at 1 # note that, by design, dlib's fhog face detector scores are >0 but not capped at 1
@ -115,13 +103,17 @@ class DlibClient(Detector):
max(0, top) : min(bottom, img.shape[0]), max(0, left) : min(right, img.shape[1]) max(0, top) : min(bottom, img.shape[0]), max(0, left) : min(right, img.shape[1])
] ]
img_region = [left, top, right - left, bottom - top] img_region = FacialAreaRegion(x=left, y=right, w=right - left, h=bottom - top)
confidence = scores[idx] confidence = scores[idx]
if align: if align:
img_shape = sp(img, detections[idx]) img_shape = sp(img, detections[idx])
detected_face = dlib.get_face_chip(img, img_shape, size=detected_face.shape[0]) detected_face = dlib.get_face_chip(img, img_shape, size=detected_face.shape[0])
resp.append((detected_face, img_region, confidence)) detected_face_obj = DetectedFace(
img=detected_face, facial_area=img_region, confidence=confidence
)
resp.append(detected_face_obj)
return resp return resp

View File

@ -1,7 +1,7 @@
from typing import Any, Union, List, Tuple from typing import Any, Union, List
import cv2 import cv2
import numpy as np import numpy as np
from deepface.models.Detector import Detector from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection from deepface.modules import detection
# Link -> https://github.com/timesler/facenet-pytorch # Link -> https://github.com/timesler/facenet-pytorch
@ -12,33 +12,22 @@ class FastMtCnnClient(Detector):
def __init__(self): def __init__(self):
self.model = self.build_model() self.model = self.build_model()
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect and align face with mtcnn Detect and align face with mtcnn
Args: Args:
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): default is true
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[DetectedFace]): A list of DetectedFace objects
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h] - confidence (float): The confidence score associated with the detected face.
- 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 = [] resp = []
detected_face = None detected_face = None
img_region = [0, 0, img.shape[1], img.shape[0]]
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # mtcnn expects RGB but OpenCV read BGR img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # mtcnn expects RGB but OpenCV read BGR
detections = self.model.detect( detections = self.model.detect(
@ -49,7 +38,7 @@ class FastMtCnnClient(Detector):
for current_detection in zip(*detections): for current_detection in zip(*detections):
x, y, w, h = xyxy_to_xywh(current_detection[0]) x, y, w, h = xyxy_to_xywh(current_detection[0])
detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
img_region = [x, y, w, h] img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = current_detection[1] confidence = current_detection[1]
if align: if align:
@ -59,7 +48,11 @@ class FastMtCnnClient(Detector):
img=detected_face, left_eye=left_eye, right_eye=right_eye img=detected_face, left_eye=left_eye, right_eye=right_eye
) )
resp.append((detected_face, img_region, confidence)) detected_face_obj = DetectedFace(
img=detected_face, facial_area=img_region, confidence=confidence
)
resp.append(detected_face_obj)
return resp return resp

View File

@ -1,6 +1,6 @@
from typing import Any, List, Tuple from typing import Any, List
import numpy as np import numpy as np
from deepface.models.Detector import Detector from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection from deepface.modules import detection
# Link - https://google.github.io/mediapipe/solutions/face_detection # Link - https://google.github.io/mediapipe/solutions/face_detection
@ -29,28 +29,18 @@ class MediaPipeClient(Detector):
face_detection = mp_face_detection.FaceDetection(min_detection_confidence=0.7) face_detection = mp_face_detection.FaceDetection(min_detection_confidence=0.7)
return face_detection return face_detection
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect and align face with mediapipe Detect and align face with mediapipe
Args: Args:
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): default is true
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[DetectedFace): A list of DetectedFace objects
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h] - confidence (float): The confidence score associated with the detected face.
- 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 = [] resp = []
@ -85,13 +75,19 @@ class MediaPipeClient(Detector):
if x > 0 and y > 0: if x > 0 and y > 0:
detected_face = img[y : y + h, x : x + w] detected_face = img[y : y + h, x : x + w]
img_region = [x, y, w, h] img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
if align: if align:
detected_face = detection.align_face( detected_face = detection.align_face(
img=detected_face, left_eye=left_eye, right_eye=right_eye img=detected_face, left_eye=left_eye, right_eye=right_eye
) )
resp.append((detected_face, img_region, confidence)) detected_face_obj = DetectedFace(
img=detected_face,
facial_area=img_region,
confidence=confidence,
)
resp.append(detected_face_obj)
return resp return resp

View File

@ -1,8 +1,8 @@
from typing import List, Tuple from typing import List
import cv2 import cv2
import numpy as np import numpy as np
from mtcnn import MTCNN from mtcnn import MTCNN
from deepface.models.Detector import Detector from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection from deepface.modules import detection
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
@ -14,34 +14,23 @@ class MtCnnClient(Detector):
def __init__(self): def __init__(self):
self.model = MTCNN() self.model = MTCNN()
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect and align face with mtcnn Detect and align face with mtcnn
Args: Args:
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): default is true
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[DetectedFace]): A list of DetectedFace objects
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h] - confidence (float): The confidence score associated with the detected face.
- 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 = [] resp = []
detected_face = None detected_face = None
img_region = [0, 0, img.shape[1], img.shape[0]]
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # mtcnn expects RGB but OpenCV read BGR img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # mtcnn expects RGB but OpenCV read BGR
detections = self.model.detect_faces(img_rgb) detections = self.model.detect_faces(img_rgb)
@ -51,7 +40,7 @@ class MtCnnClient(Detector):
for current_detection in detections: for current_detection in detections:
x, y, w, h = current_detection["box"] x, y, w, h = current_detection["box"]
detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
img_region = [x, y, w, h] img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = current_detection["confidence"] confidence = current_detection["confidence"]
if align: if align:
@ -62,6 +51,10 @@ class MtCnnClient(Detector):
img=detected_face, left_eye=left_eye, right_eye=right_eye img=detected_face, left_eye=left_eye, right_eye=right_eye
) )
resp.append((detected_face, img_region, confidence)) detected_face_obj = DetectedFace(
img=detected_face, facial_area=img_region, confidence=confidence
)
resp.append(detected_face_obj)
return resp return resp

View File

@ -1,8 +1,8 @@
import os import os
from typing import Any, List, Tuple from typing import Any, List
import cv2 import cv2
import numpy as np import numpy as np
from deepface.models.Detector import Detector from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection from deepface.modules import detection
@ -25,9 +25,7 @@ class OpenCvClient(Detector):
detector["eye_detector"] = self.__build_cascade("haarcascade_eye") detector["eye_detector"] = self.__build_cascade("haarcascade_eye")
return detector return detector
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect and align face with opencv Detect and align face with opencv
Args: Args:
@ -35,24 +33,15 @@ class OpenCvClient(Detector):
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): default is true
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h] - confidence (float): The confidence score associated with the detected face.
- 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 = [] resp = []
detected_face = None detected_face = None
img_region = [0, 0, img.shape[1], img.shape[0]]
faces = [] faces = []
try: try:
@ -73,9 +62,13 @@ class OpenCvClient(Detector):
left_eye, right_eye = self.find_eyes(img=detected_face) left_eye, right_eye = self.find_eyes(img=detected_face)
detected_face = detection.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] detected_face_obj = DetectedFace(
img=detected_face,
facial_area=FacialAreaRegion(x, y, w, h),
confidence=confidence,
)
resp.append((detected_face, img_region, confidence)) resp.append(detected_face_obj)
return resp return resp

View File

@ -1,36 +1,26 @@
from typing import List, Tuple from typing import List
import numpy as np import numpy as np
from retinaface import RetinaFace as rf from retinaface import RetinaFace as rf
from retinaface.commons import postprocess from retinaface.commons import postprocess
from deepface.models.Detector import Detector from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
class RetinaFaceClient(Detector): class RetinaFaceClient(Detector):
def __init__(self): def __init__(self):
self.model = rf.build_model() self.model = rf.build_model()
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect and align face with retinaface Detect and align face with retinaface
Args: Args:
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): default is true
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[DetectedFace]): A list of DetectedFace object
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h] - confidence (float): The confidence score associated with the detected face.
- 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 = [] resp = []
@ -45,7 +35,7 @@ class RetinaFaceClient(Detector):
h = facial_area[3] - y h = facial_area[3] - y
x = facial_area[0] x = facial_area[0]
w = facial_area[2] - x w = facial_area[2] - x
img_region = [x, y, w, h] img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = identity["score"] confidence = identity["score"]
# detected_face = img[int(y):int(y+h), int(x):int(x+w)] #opencv # detected_face = img[int(y):int(y+h), int(x):int(x+w)] #opencv
@ -65,6 +55,12 @@ class RetinaFaceClient(Detector):
detected_face, right_eye, left_eye, nose detected_face, right_eye, left_eye, nose
) )
resp.append((detected_face, img_region, confidence)) detected_face_obj = DetectedFace(
img=detected_face,
facial_area=img_region,
confidence=confidence,
)
resp.append(detected_face_obj)
return resp return resp

View File

@ -1,4 +1,4 @@
from typing import List, Tuple from typing import List
import os import os
import gdown import gdown
import cv2 import cv2
@ -6,7 +6,7 @@ import pandas as pd
import numpy as np import numpy as np
from deepface.detectors import OpenCv from deepface.detectors import OpenCv
from deepface.commons import functions from deepface.commons import functions
from deepface.models.Detector import Detector from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection from deepface.modules import detection
from deepface.commons.logger import Logger from deepface.commons.logger import Logger
@ -71,33 +71,22 @@ class SsdClient(Detector):
return detector return detector
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect and align face with ssd Detect and align face with ssd
Args: Args:
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): default is true
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[DetectedFace]): A list of DetectedFace object
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h] - confidence (float): The confidence score associated with the detected face.
- 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 = [] resp = []
detected_face = None detected_face = None
img_region = [0, 0, img.shape[1], img.shape[0]]
ssd_labels = ["img_id", "is_face", "confidence", "left", "top", "right", "bottom"] ssd_labels = ["img_id", "is_face", "confidence", "left", "top", "right", "bottom"]
@ -141,12 +130,14 @@ class SsdClient(Detector):
int(top * aspect_ratio_y) : int(bottom * aspect_ratio_y), int(top * aspect_ratio_y) : int(bottom * aspect_ratio_y),
int(left * aspect_ratio_x) : int(right * aspect_ratio_x), int(left * aspect_ratio_x) : int(right * aspect_ratio_x),
] ]
img_region = [
int(left * aspect_ratio_x), face_region = FacialAreaRegion(
int(top * aspect_ratio_y), x=int(left * aspect_ratio_x),
int(right * aspect_ratio_x) - int(left * aspect_ratio_x), y=int(top * aspect_ratio_y),
int(bottom * aspect_ratio_y) - int(top * aspect_ratio_y), w=int(right * aspect_ratio_x) - int(left * aspect_ratio_x),
] h=int(bottom * aspect_ratio_y) - int(top * aspect_ratio_y),
)
confidence = instance["confidence"] confidence = instance["confidence"]
if align: if align:
@ -156,5 +147,11 @@ class SsdClient(Detector):
img=detected_face, left_eye=left_eye, right_eye=right_eye img=detected_face, left_eye=left_eye, right_eye=right_eye
) )
resp.append((detected_face, img_region, confidence)) detected_face_obj = DetectedFace(
img=detected_face,
facial_area=face_region,
confidence=confidence,
)
resp.append(detected_face_obj)
return resp return resp

View File

@ -1,6 +1,8 @@
from typing import Any, List, Tuple import os
from typing import Any, List
import numpy as np import numpy as np
from deepface.models.Detector import Detector import gdown
from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection from deepface.modules import detection
from deepface.commons.logger import Logger from deepface.commons.logger import Logger
@ -9,7 +11,7 @@ logger = Logger()
# Model's weights paths # Model's weights paths
PATH = "/.deepface/weights/yolov8n-face.pt" PATH = "/.deepface/weights/yolov8n-face.pt"
# Google Drive URL # Google Drive URL from repo (https://github.com/derronqi/yolov8-face) ~6MB
WEIGHT_URL = "https://drive.google.com/uc?id=1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb" WEIGHT_URL = "https://drive.google.com/uc?id=1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb"
# Confidence thresholds for landmarks detection # Confidence thresholds for landmarks detection
@ -27,8 +29,6 @@ class YoloClient(Detector):
Returns: Returns:
model (Any) model (Any)
""" """
import gdown
import os
# Import the Ultralytics YOLO model # Import the Ultralytics YOLO model
try: try:
@ -51,9 +51,7 @@ class YoloClient(Detector):
# Return face_detector # Return face_detector
return YOLO(weight_path) return YOLO(weight_path)
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = False) -> List[DetectedFace]:
self, img: np.ndarray, align: bool = False
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect and align face with yolo Detect and align face with yolo
Args: Args:
@ -61,19 +59,11 @@ class YoloClient(Detector):
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): default is true
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h] - confidence (float): The confidence score associated with the detected face.
- 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 = [] resp = []
@ -87,6 +77,7 @@ class YoloClient(Detector):
confidence = result.boxes.conf.tolist()[0] confidence = result.boxes.conf.tolist()[0]
x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h)
region = FacialAreaRegion(x=x, y=y, w=w, h=h)
detected_face = img[y : y + h, x : x + w].copy() detected_face = img[y : y + h, x : x + w].copy()
if align: if align:
@ -103,6 +94,10 @@ class YoloClient(Detector):
detected_face = detection.align_face( detected_face = detection.align_face(
img=detected_face, left_eye=left_eye[0].cpu(), right_eye=right_eye[0].cpu() img=detected_face, left_eye=left_eye[0].cpu(), right_eye=right_eye[0].cpu()
) )
resp.append((detected_face, [x, y, w, h], confidence))
detected_face_obj = DetectedFace(
img=detected_face, facial_area=region, confidence=confidence
)
resp.append(detected_face_obj)
return resp return resp

View File

@ -1,10 +1,10 @@
import os import os
from typing import Any, List, Tuple from typing import Any, List
import cv2 import cv2
import numpy as np import numpy as np
import gdown import gdown
from deepface.commons import functions from deepface.commons import functions
from deepface.models.Detector import Detector from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection from deepface.modules import detection
from deepface.commons.logger import Logger from deepface.commons.logger import Logger
@ -49,35 +49,24 @@ class YuNetClient(Detector):
) from err ) from err
return face_detector return face_detector
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect and align face with yunet Detect and align face with yunet
Args: Args:
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): default is true
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[DetectedFace]): A list of DetectedFace objects
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
a list of floats e.g. [x, y, w, h] - confidence (float): The confidence score associated with the detected face.
- 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. # FaceDetector.detect_faces does not support score_threshold parameter.
# We can set it via environment variable. # We can set it via environment variable.
score_threshold = float(os.environ.get("yunet_score_threshold", "0.9")) score_threshold = float(os.environ.get("yunet_score_threshold", "0.9"))
resp = [] resp = []
detected_face = None detected_face = None
img_region = [0, 0, img.shape[1], img.shape[0]]
faces = [] faces = []
height, width = img.shape[0], img.shape[1] height, width = img.shape[0], img.shape[1]
# resize image if it is too large (Yunet fails to detect faces on large input sometimes) # resize image if it is too large (Yunet fails to detect faces on large input sometimes)
@ -127,8 +116,12 @@ class YuNetClient(Detector):
confidence = face[-1] confidence = face[-1]
confidence = f"{confidence:.2f}" confidence = f"{confidence:.2f}"
detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
img_region = [x, y, w, h] img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
if align: if align:
detected_face = detection.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))
detected_face_obj = DetectedFace(
img=detected_face, facial_area=img_region, confidence=confidence
)
resp.append(detected_face_obj)
return resp return resp

View File

@ -1,4 +1,4 @@
from typing import List, Tuple from typing import List
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import numpy as np import numpy as np
@ -8,27 +8,42 @@ import numpy as np
# pylint: disable=unnecessary-pass, too-few-public-methods # pylint: disable=unnecessary-pass, too-few-public-methods
class Detector(ABC): class Detector(ABC):
@abstractmethod @abstractmethod
def detect_faces( def detect_faces(self, img: np.ndarray, align: bool = True) -> List["DetectedFace"]:
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
""" """
Detect faces from a given image Detect faces from a given image
Args: Args:
img (np.ndarray): pre-loaded image as a NumPy array img (np.ndarray): pre-loaded image as a NumPy array
align (bool): enable or disable alignment after face detection align (bool): enable or disable alignment after face detection
Returns: Returns:
results (List[Tuple[np.ndarray, List[float], float]]): A list of tuples results (List[DetectedFace]): A list of DetectedFace object
where each tuple contains: where each object contains:
- detected_face (np.ndarray): The detected face as a NumPy array. - face (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - face_region (List[float]): The image region represented as
a list of floats e.g. [x, y, w, h] a list of floats e.g. [x, y, w, h]
- confidence (float): The confidence score associated with the detected face. - 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),
]
""" """
pass pass
class FacialAreaRegion:
x: int
y: int
w: int
h: int
def __init__(self, x: int, y: int, w: int, h: int):
self.x = x
self.y = y
self.w = w
self.h = h
class DetectedFace:
img: np.ndarray
facial_area: FacialAreaRegion
confidence: float
def __init__(self, img: np.ndarray, facial_area: FacialAreaRegion, confidence: float):
self.img = img
self.facial_area = facial_area
self.confidence = confidence

View File

@ -8,7 +8,7 @@ with open("requirements.txt", "r", encoding="utf-8") as f:
setuptools.setup( setuptools.setup(
name="deepface", name="deepface",
version="0.0.83", version="0.0.84",
author="Sefik Ilkin Serengil", author="Sefik Ilkin Serengil",
author_email="serengil@gmail.com", author_email="serengil@gmail.com",
description="A Lightweight Face Recognition and Facial Attribute Analysis Framework (Age, Gender, Emotion, Race) for Python", description="A Lightweight Face Recognition and Facial Attribute Analysis Framework (Age, Gender, Emotion, Race) for Python",

View File

@ -20,8 +20,7 @@ model_names = [
"SFace", "SFace",
] ]
detector_backends = ["opencv", "ssd", "dlib", "mtcnn", "retinaface", "yunet"] detector_backends = ["opencv", "ssd", "dlib", "mtcnn", "retinaface", "yunet", "yolov8"]
# verification # verification
for model_name in model_names: for model_name in model_names:
@ -45,7 +44,6 @@ dfs = DeepFace.find(
for df in dfs: for df in dfs:
logger.info(df) logger.info(df)
# extract faces # extract faces
for detector_backend in detector_backends: for detector_backend in detector_backends:
face_objs = DeepFace.extract_faces( face_objs = DeepFace.extract_faces(