Skip to content

Commit

Permalink
Merge pull request #978 from serengil/feat-task-2701-return-obj-for-d…
Browse files Browse the repository at this point in the history
…etectors

pretty return type for detect faces
  • Loading branch information
serengil authored Jan 27, 2024
2 parents 13f33de + 69c727a commit 35025cd
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 235 deletions.
21 changes: 13 additions & 8 deletions deepface/commons/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

# package dependencies
from deepface.detectors import DetectorWrapper
from deepface.models.Detector import DetectedFace, FacialAreaRegion
from deepface.commons.logger import Logger

logger = Logger(module="commons.functions")
Expand Down Expand Up @@ -170,10 +171,11 @@ def extract_faces(

# img might be path, base64 or numpy array. Convert it to numpy whatever it is.
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":
face_objs = [(img, img_region, 0)]
face_objs = [DetectedFace(img=img, facial_area=base_region, confidence=0)]
else:
face_objs = DetectorWrapper.detect_faces(detector_backend, img, align)

Expand All @@ -192,9 +194,12 @@ def extract_faces(
)

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 grayscale is True:
current_img = cv2.cvtColor(current_img, cv2.COLOR_BGR2GRAY)
Expand Down Expand Up @@ -245,10 +250,10 @@ def extract_faces(

# int cast is for the exception - object of type 'float32' is not JSON serializable
region_obj = {
"x": int(current_region[0]),
"y": int(current_region[1]),
"w": int(current_region[2]),
"h": int(current_region[3]),
"x": current_region.x,
"y": current_region.y,
"w": current_region.w,
"h": current_region.h,
}

extracted_face = (img_pixels, region_obj, confidence)
Expand Down
22 changes: 7 additions & 15 deletions deepface/detectors/DetectorWrapper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any
from typing import Any, List
import numpy as np
from deepface.models.Detector import Detector
from deepface.models.Detector import Detector, DetectedFace
from deepface.detectors import (
FastMtCnn,
MediaPipe,
Expand Down Expand Up @@ -52,27 +52,19 @@ def build_model(detector_backend: str) -> Any:
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
Args:
detector_backend (str): detector name
img (np.ndarray): pre-loaded image
alig (bool): enable or disable alignment after detection
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]
results (List[DetectedFace]): A list of DetectedFace objects
where each object contains:
- img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as 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)
36 changes: 14 additions & 22 deletions deepface/detectors/Dlib.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import List, Tuple
from typing import List
import os
import bz2
import gdown
import numpy as np
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

logger = Logger(module="detectors.DlibWrapper")
Expand Down Expand Up @@ -56,29 +56,19 @@ def build_model(self) -> dict:
detector["sp"] = sp
return detector

def detect_faces(
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
"""
Detect and align face with dlib
Args:
face_detector (Any): dlib face detector object
img (np.ndarray): pre-loaded image
align (bool): default is true
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),
]
results (List[DetectedFace]): A list of DetectedFace objects
where each object contains:
- img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face.
"""
# this is not a must dependency. do not import it in the global level.
try:
Expand All @@ -95,8 +85,6 @@ def detect_faces(

detected_face = None

img_region = [0, 0, img.shape[1], img.shape[0]]

face_detector = self.model["face_detector"]

# note that, by design, dlib's fhog face detector scores are >0 but not capped at 1
Expand All @@ -115,13 +103,17 @@ def detect_faces(
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]

if align:
img_shape = sp(img, detections[idx])
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
35 changes: 14 additions & 21 deletions deepface/detectors/FastMtCnn.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, Union, List, Tuple
from typing import Any, Union, List
import cv2
import numpy as np
from deepface.models.Detector import Detector
from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection

# Link -> https://github.com/timesler/facenet-pytorch
Expand All @@ -12,33 +12,22 @@ class FastMtCnnClient(Detector):
def __init__(self):
self.model = self.build_model()

def detect_faces(
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
"""
Detect and align face with mtcnn
Args:
img (np.ndarray): pre-loaded image
align (bool): default is true
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),
]
results (List[DetectedFace]): A list of DetectedFace objects
where each object contains:
- img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face.
"""
resp = []

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
detections = self.model.detect(
Expand All @@ -49,7 +38,7 @@ def detect_faces(
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]
img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = current_detection[1]

if align:
Expand All @@ -59,7 +48,11 @@ def detect_faces(
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

Expand Down
36 changes: 16 additions & 20 deletions deepface/detectors/MediaPipe.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any, List, Tuple
from typing import Any, List
import numpy as np
from deepface.models.Detector import Detector
from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection

# Link - https://google.github.io/mediapipe/solutions/face_detection
Expand Down Expand Up @@ -29,28 +29,18 @@ def build_model(self) -> Any:
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[Tuple[np.ndarray, List[float], float]]:
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
"""
Detect and align face with mediapipe
Args:
img (np.ndarray): pre-loaded image
align (bool): default is true
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),
]
results (List[DetectedFace): A list of DetectedFace objects
where each object contains:
- img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face.
"""
resp = []

Expand Down Expand Up @@ -85,13 +75,19 @@ def detect_faces(

if x > 0 and y > 0:
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:
detected_face = detection.align_face(
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
35 changes: 14 additions & 21 deletions deepface/detectors/MtCnn.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import List, Tuple
from typing import List
import cv2
import numpy as np
from mtcnn import MTCNN
from deepface.models.Detector import Detector
from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection

# pylint: disable=too-few-public-methods
Expand All @@ -14,34 +14,23 @@ class MtCnnClient(Detector):
def __init__(self):
self.model = MTCNN()

def detect_faces(
self, img: np.ndarray, align: bool = True
) -> List[Tuple[np.ndarray, List[float], float]]:
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]:
"""
Detect and align face with mtcnn
Args:
img (np.ndarray): pre-loaded image
align (bool): default is true
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),
]
results (List[DetectedFace]): A list of DetectedFace objects
where each object contains:
- img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face.
"""

resp = []

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
detections = self.model.detect_faces(img_rgb)
Expand All @@ -51,7 +40,7 @@ def detect_faces(
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]
img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = current_detection["confidence"]

if align:
Expand All @@ -62,6 +51,10 @@ def detect_faces(
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
Loading

0 comments on commit 35025cd

Please sign in to comment.