|
|
|
from typing import Any, Dict, List, Tuple, Union |
|
|
|
|
|
import numpy as np |
|
import cv2 |
|
from PIL import Image |
|
|
|
|
|
from deepface.models.Detector import DetectedFace, FacialAreaRegion |
|
from deepface.detectors import DetectorWrapper |
|
from deepface.commons import image_utils |
|
from deepface.commons import logger as log |
|
|
|
logger = log.get_singletonish_logger() |
|
|
|
|
|
|
|
|
|
def extract_faces( |
|
img_path: Union[str, np.ndarray], |
|
detector_backend: str = "opencv", |
|
enforce_detection: bool = True, |
|
align: bool = True, |
|
expand_percentage: int = 0, |
|
grayscale: bool = False, |
|
) -> List[Dict[str, Any]]: |
|
""" |
|
Extract faces from a given image |
|
|
|
Args: |
|
img_path (str or np.ndarray): Path to the first image. Accepts exact image path |
|
as a string, numpy array (BGR), or base64 encoded images. |
|
|
|
detector_backend (string): face detector backend. Options: 'opencv', 'retinaface', |
|
'mtcnn', 'ssd', 'dlib', 'mediapipe', 'yolov8', 'centerface' or 'skip' |
|
(default is opencv) |
|
|
|
enforce_detection (boolean): If no face is detected in an image, raise an exception. |
|
Default is True. Set to False to avoid the exception for low-resolution images. |
|
|
|
align (bool): Flag to enable face alignment (default is True). |
|
|
|
expand_percentage (int): expand detected facial area with a percentage |
|
|
|
grayscale (boolean): Flag to convert the image to grayscale before |
|
processing (default is False). |
|
|
|
Returns: |
|
results (List[Dict[str, Any]]): A list of dictionaries, where each dictionary contains: |
|
|
|
- "face" (np.ndarray): The detected face as a NumPy array in RGB format. |
|
|
|
- "facial_area" (Dict[str, Any]): The detected face's regions as a dictionary containing: |
|
- keys 'x', 'y', 'w', 'h' with int values |
|
- keys 'left_eye', 'right_eye' with a tuple of 2 ints as values. |
|
left eye and right eye are eyes on the left and right respectively with respect |
|
to the person itself instead of observer. |
|
|
|
- "confidence" (float): The confidence score associated with the detected face. |
|
""" |
|
|
|
resp_objs = [] |
|
|
|
|
|
img, img_name = image_utils.load_image(img_path) |
|
|
|
if img is None: |
|
raise ValueError(f"Exception while loading {img_name}") |
|
|
|
base_region = FacialAreaRegion(x=0, y=0, w=img.shape[1], h=img.shape[0], confidence=0) |
|
|
|
if detector_backend == "skip": |
|
face_objs = [DetectedFace(img=img, facial_area=base_region, confidence=0)] |
|
else: |
|
face_objs = DetectorWrapper.detect_faces( |
|
detector_backend=detector_backend, |
|
img=img, |
|
align=align, |
|
expand_percentage=expand_percentage, |
|
) |
|
|
|
|
|
if len(face_objs) == 0 and enforce_detection is True: |
|
if img_name is not None: |
|
raise ValueError( |
|
f"Face could not be detected in {img_name}." |
|
"Please confirm that the picture is a face photo " |
|
"or consider to set enforce_detection param to False." |
|
) |
|
else: |
|
raise ValueError( |
|
"Face could not be detected. Please confirm that the picture is a face photo " |
|
"or consider to set enforce_detection param to False." |
|
) |
|
|
|
if len(face_objs) == 0 and enforce_detection is False: |
|
face_objs = [DetectedFace(img=img, facial_area=base_region, confidence=0)] |
|
|
|
for face_obj in face_objs: |
|
current_img = face_obj.img |
|
current_region = face_obj.facial_area |
|
|
|
if current_img.shape[0] == 0 or current_img.shape[1] == 0: |
|
continue |
|
|
|
if grayscale is True: |
|
current_img = cv2.cvtColor(current_img, cv2.COLOR_BGR2GRAY) |
|
|
|
current_img = current_img / 255 |
|
|
|
resp_objs.append( |
|
{ |
|
"face": current_img[:, :, ::-1], |
|
"facial_area": { |
|
"x": int(current_region.x), |
|
"y": int(current_region.y), |
|
"w": int(current_region.w), |
|
"h": int(current_region.h), |
|
"left_eye": current_region.left_eye, |
|
"right_eye": current_region.right_eye, |
|
}, |
|
"confidence": round(current_region.confidence, 2), |
|
} |
|
) |
|
|
|
if len(resp_objs) == 0 and enforce_detection == True: |
|
raise ValueError( |
|
f"Exception while extracting faces from {img_name}." |
|
"Consider to set enforce_detection arg to False." |
|
) |
|
|
|
return resp_objs |
|
|
|
|
|
def align_face( |
|
img: np.ndarray, |
|
left_eye: Union[list, tuple], |
|
right_eye: Union[list, tuple], |
|
) -> Tuple[np.ndarray, float]: |
|
""" |
|
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 person itself |
|
right_eye(list or tuple): coordinates of right eye with respect to the person itself |
|
Returns: |
|
img (np.ndarray): aligned facial image |
|
""" |
|
|
|
if left_eye is None or right_eye is None: |
|
return img, 0 |
|
|
|
|
|
if img.shape[0] == 0 or img.shape[1] == 0: |
|
return img, 0 |
|
|
|
angle = float(np.degrees(np.arctan2(left_eye[1] - right_eye[1], left_eye[0] - right_eye[0]))) |
|
img = np.array(Image.fromarray(img).rotate(angle)) |
|
return img, angle |
|
|