adding expand percentage argument for detection

This commit is contained in:
Sefik Ilkin Serengil 2024-01-31 23:43:30 +00:00
parent 9494d47e31
commit 96d29ab069
17 changed files with 314 additions and 79 deletions

View File

@ -58,6 +58,7 @@ def verify(
distance_metric: str = "cosine", distance_metric: str = "cosine",
enforce_detection: bool = True, enforce_detection: bool = True,
align: bool = True, align: bool = True,
expand_percentage: int = 0,
normalization: str = "base", normalization: str = "base",
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
@ -83,6 +84,8 @@ def verify(
align (bool): Flag to enable face alignment (default is True). align (bool): Flag to enable face alignment (default is True).
expand_percentage (int): expand detected facial area with a percentage (default is 0).
normalization (string): Normalize the input image before feeding it to the model. normalization (string): Normalize the input image before feeding it to the model.
Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace (default is base) Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace (default is base)
@ -119,6 +122,7 @@ def verify(
distance_metric=distance_metric, distance_metric=distance_metric,
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
align=align, align=align,
expand_percentage=expand_percentage,
normalization=normalization, normalization=normalization,
) )
@ -129,6 +133,7 @@ def analyze(
enforce_detection: bool = True, enforce_detection: bool = True,
detector_backend: str = "opencv", detector_backend: str = "opencv",
align: bool = True, align: bool = True,
expand_percentage: int = 0,
silent: bool = False, silent: bool = False,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
""" """
@ -152,6 +157,8 @@ def analyze(
align (boolean): Perform alignment based on the eye positions (default is True). align (boolean): Perform alignment based on the eye positions (default is True).
expand_percentage (int): expand detected facial area with a percentage (default is 0).
silent (boolean): Suppress or allow some log messages for a quieter analysis process silent (boolean): Suppress or allow some log messages for a quieter analysis process
(default is False). (default is False).
@ -209,6 +216,7 @@ def analyze(
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
detector_backend=detector_backend, detector_backend=detector_backend,
align=align, align=align,
expand_percentage=expand_percentage,
silent=silent, silent=silent,
) )
@ -221,6 +229,7 @@ def find(
enforce_detection: bool = True, enforce_detection: bool = True,
detector_backend: str = "opencv", detector_backend: str = "opencv",
align: bool = True, align: bool = True,
expand_percentage: int = 0,
threshold: Optional[float] = None, threshold: Optional[float] = None,
normalization: str = "base", normalization: str = "base",
silent: bool = False, silent: bool = False,
@ -249,6 +258,8 @@ def find(
align (boolean): Perform alignment based on the eye positions (default is True). align (boolean): Perform alignment based on the eye positions (default is True).
expand_percentage (int): expand detected facial area with a percentage (default is 0).
threshold (float): Specify a threshold to determine whether a pair represents the same threshold (float): Specify a threshold to determine whether a pair represents the same
person or different individuals. This threshold is used for comparing distances. person or different individuals. This threshold is used for comparing distances.
If left unset, default pre-tuned threshold values will be applied based on the specified If left unset, default pre-tuned threshold values will be applied based on the specified
@ -286,6 +297,7 @@ def find(
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
detector_backend=detector_backend, detector_backend=detector_backend,
align=align, align=align,
expand_percentage=expand_percentage,
threshold=threshold, threshold=threshold,
normalization=normalization, normalization=normalization,
silent=silent, silent=silent,
@ -298,6 +310,7 @@ def represent(
enforce_detection: bool = True, enforce_detection: bool = True,
detector_backend: str = "opencv", detector_backend: str = "opencv",
align: bool = True, align: bool = True,
expand_percentage: int = 0,
normalization: str = "base", normalization: str = "base",
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
""" """
@ -320,6 +333,8 @@ def represent(
align (boolean): Perform alignment based on the eye positions (default is True). align (boolean): Perform alignment based on the eye positions (default is True).
expand_percentage (int): expand detected facial area with a percentage (default is 0).
normalization (string): Normalize the input image before feeding it to the model. normalization (string): Normalize the input image before feeding it to the model.
Default is base. Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace Default is base. Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace
(default is base). (default is base).
@ -346,6 +361,7 @@ def represent(
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
detector_backend=detector_backend, detector_backend=detector_backend,
align=align, align=align,
expand_percentage=expand_percentage,
normalization=normalization, normalization=normalization,
) )
@ -409,6 +425,7 @@ def extract_faces(
detector_backend: str = "opencv", detector_backend: str = "opencv",
enforce_detection: bool = True, enforce_detection: bool = True,
align: bool = True, align: bool = True,
expand_percentage: int = 0,
grayscale: bool = False, grayscale: bool = False,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
""" """
@ -429,6 +446,8 @@ def extract_faces(
align (bool): Flag to enable face alignment (default is True). align (bool): Flag to enable face alignment (default is True).
expand_percentage (int): expand detected facial area with a percentage (default is 0).
grayscale (boolean): Flag to convert the image to grayscale before grayscale (boolean): Flag to convert the image to grayscale before
processing (default is False). processing (default is False).
@ -448,6 +467,7 @@ def extract_faces(
detector_backend=detector_backend, detector_backend=detector_backend,
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
align=align, align=align,
expand_percentage=expand_percentage,
grayscale=grayscale, grayscale=grayscale,
human_readable=True, human_readable=True,
) )

View File

@ -12,6 +12,9 @@ from deepface.detectors import (
Yolo, Yolo,
YuNet, YuNet,
) )
from deepface.commons.logger import Logger
logger = Logger(module="deepface/detectors/DetectorWrapper.py")
def build_model(detector_backend: str) -> Any: def build_model(detector_backend: str) -> Any:
@ -52,19 +55,35 @@ 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[DetectedFace]: def detect_faces(
detector_backend: str, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
Detect face(s) from a given image Detect face(s) from a given image
Args: Args:
detector_backend (str): detector name detector_backend (str): detector name
img (np.ndarray): pre-loaded image img (np.ndarray): pre-loaded image
alig (bool): enable or disable alignment after detection
align (bool): enable or disable alignment after detection
expand_percentage (int): expand detected facial area with a percentage (default is 0).
Returns: Returns:
results (List[DetectedFace]): A list of DetectedFace objects results (List[DetectedFace]): A list of DetectedFace objects
where each object contains: 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 - img (np.ndarray): The detected face as a NumPy array.
- confidence (float): The confidence score associated with the detected face.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face.
""" """
face_detector: Detector = build_model(detector_backend) face_detector: Detector = build_model(detector_backend)
return face_detector.detect_faces(img=img, align=align) if expand_percentage < 0:
logger.warn(
f"Expand percentage cannot be negative but you set it to {expand_percentage}."
"Overwritten it to 0."
)
expand_percentage = 0
return face_detector.detect_faces(img=img, align=align, expand_percentage=expand_percentage)

View File

@ -56,18 +56,27 @@ class DlibClient(Detector):
detector["sp"] = sp detector["sp"] = sp
return detector return detector
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]: def detect_faces(
self, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
Detect and align face with dlib Detect and align face with dlib
Args: Args:
face_detector (Any): dlib face detector object img (np.ndarray): pre-loaded image as numpy array
img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[DetectedFace]): A list of DetectedFace objects results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- img (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face. - confidence (float): The confidence score associated with the detected face.
""" """
# 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.
@ -79,6 +88,12 @@ class DlibClient(Detector):
"Please install using 'pip install dlib' " "Please install using 'pip install dlib' "
) from e ) from e
if expand_percentage != 0:
logger.warn(
f"You set expand_percentage argument to {expand_percentage},"
"but dlib hog handles detection by itself"
)
resp = [] resp = []
sp = self.model["sp"] sp = self.model["sp"]

View File

@ -12,17 +12,27 @@ class FastMtCnnClient(Detector):
def __init__(self): def __init__(self):
self.model = self.build_model() self.model = self.build_model()
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]: def detect_faces(
self, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
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 as numpy array
align (bool): default is true
align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[DetectedFace]): A list of DetectedFace objects results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- img (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face. - confidence (float): The confidence score associated with the detected face.
""" """
resp = [] resp = []
@ -37,7 +47,16 @@ 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)]
# expand the facial area to be extracted and stay within img.shape limits
x2 = max(0, x - int((w * expand_percentage) / 100)) # expand left
y2 = max(0, y - int((h * expand_percentage) / 100)) # expand top
w2 = min(img.shape[1], w + int((w * expand_percentage) / 100)) # expand right
h2 = min(img.shape[0], h + int((h * expand_percentage) / 100)) # expand bottom
# detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
detected_face = img[int(y2) : int(y2 + h2), int(x2) : int(x2 + w2)]
img_region = FacialAreaRegion(x=x, y=y, w=w, h=h) img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = current_detection[1] confidence = current_detection[1]

View File

@ -29,17 +29,27 @@ 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(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]: def detect_faces(
self, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
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 as numpy array
align (bool): default is true
align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[DetectedFace): A list of DetectedFace objects results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- img (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face. - confidence (float): The confidence score associated with the detected face.
""" """
resp = [] resp = []
@ -74,7 +84,16 @@ class MediaPipeClient(Detector):
# left_ear = (int(landmarks[5].x * img_width), int(landmarks[5].y * img_height)) # left_ear = (int(landmarks[5].x * img_width), int(landmarks[5].y * img_height))
if x > 0 and y > 0: if x > 0 and y > 0:
detected_face = img[y : y + h, x : x + w]
# expand the facial area to be extracted and stay within img.shape limits
x2 = max(0, x - int((w * expand_percentage) / 100)) # expand left
y2 = max(0, y - int((h * expand_percentage) / 100)) # expand top
w2 = min(img.shape[1], w + int((w * expand_percentage) / 100)) # expand right
h2 = min(img.shape[0], h + int((h * expand_percentage) / 100)) # expand bottom
# detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
detected_face = img[int(y2) : int(y2 + h2), int(x2) : int(x2 + w2)]
img_region = FacialAreaRegion(x=x, y=y, w=w, h=h) img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
if align: if align:

View File

@ -13,17 +13,27 @@ class MtCnnClient(Detector):
def __init__(self): def __init__(self):
self.model = MTCNN() self.model = MTCNN()
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]: def detect_faces(
self, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
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 as numpy array
align (bool): default is true
align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[DetectedFace]): A list of DetectedFace objects results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- img (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face. - confidence (float): The confidence score associated with the detected face.
""" """
@ -40,7 +50,16 @@ 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)]
# expand the facial area to be extracted and stay within img.shape limits
x2 = max(0, x - int((w * expand_percentage) / 100)) # expand left
y2 = max(0, y - int((h * expand_percentage) / 100)) # expand top
w2 = min(img.shape[1], w + int((w * expand_percentage) / 100)) # expand right
h2 = min(img.shape[0], h + int((h * expand_percentage) / 100)) # expand bottom
# detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
detected_face = img[int(y2) : int(y2 + h2), int(x2) : int(x2 + w2)]
img_region = FacialAreaRegion(x=x, y=y, w=w, h=h) img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = current_detection["confidence"] confidence = current_detection["confidence"]

View File

@ -25,18 +25,27 @@ 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(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]: def detect_faces(
self, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
Detect and align face with opencv Detect and align face with opencv
Args: Args:
face_detector (Any): opencv face detector object img (np.ndarray): pre-loaded image as numpy array
img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[Tuple[DetectedFace]): A list of DetectedFace objects results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- img (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face. - confidence (float): The confidence score associated with the detected face.
""" """
resp = [] resp = []
@ -56,7 +65,15 @@ class OpenCvClient(Detector):
if len(faces) > 0: if len(faces) > 0:
for (x, y, w, h), confidence in zip(faces, scores): for (x, y, w, h), confidence in zip(faces, scores):
detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
# expand the facial area to be extracted and stay within img.shape limits
x2 = max(0, x - int((w * expand_percentage) / 100)) # expand left
y2 = max(0, y - int((h * expand_percentage) / 100)) # expand top
w2 = min(img.shape[1], w + int((w * expand_percentage) / 100)) # expand right
h2 = min(img.shape[0], h + int((h * expand_percentage) / 100)) # expand bottom
# detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
detected_face = img[int(y2) : int(y2 + h2), int(x2) : int(x2 + w2)]
if align: if align:
left_eye, right_eye = self.find_eyes(img=detected_face) left_eye, right_eye = self.find_eyes(img=detected_face)

View File

@ -9,17 +9,27 @@ class RetinaFaceClient(Detector):
def __init__(self): def __init__(self):
self.model = rf.build_model() self.model = rf.build_model()
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]: def detect_faces(
self, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
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 as numpy array
align (bool): default is true
align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[DetectedFace]): A list of DetectedFace object results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- img (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face. - confidence (float): The confidence score associated with the detected face.
""" """
resp = [] resp = []
@ -38,10 +48,14 @@ class RetinaFaceClient(Detector):
img_region = FacialAreaRegion(x=x, y=y, w=w, h=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 # expand the facial area to be extracted and stay within img.shape limits
detected_face = img[ x2 = max(0, x - int((w * expand_percentage) / 100)) # expand left
facial_area[1] : facial_area[3], facial_area[0] : facial_area[2] y2 = max(0, y - int((h * expand_percentage) / 100)) # expand top
] w2 = min(img.shape[1], w + int((w * expand_percentage) / 100)) # expand right
h2 = min(img.shape[0], h + int((h * expand_percentage) / 100)) # expand bottom
# detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
detected_face = img[int(y2) : int(y2 + h2), int(x2) : int(x2 + w2)]
if align: if align:
landmarks = identity["landmarks"] landmarks = identity["landmarks"]

View File

@ -71,17 +71,27 @@ class SsdClient(Detector):
return detector return detector
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]: def detect_faces(
self, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
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 as numpy array
align (bool): default is true
align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[DetectedFace]): A list of DetectedFace object results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- img (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face. - confidence (float): The confidence score associated with the detected face.
""" """
resp = [] resp = []
@ -92,16 +102,14 @@ class SsdClient(Detector):
target_size = (300, 300) target_size = (300, 300)
base_img = img.copy() # we will restore base_img to img later
original_size = img.shape original_size = img.shape
img = cv2.resize(img, target_size) current_img = cv2.resize(img, target_size)
aspect_ratio_x = original_size[1] / target_size[1] aspect_ratio_x = original_size[1] / target_size[1]
aspect_ratio_y = original_size[0] / target_size[0] aspect_ratio_y = original_size[0] / target_size[0]
imageBlob = cv2.dnn.blobFromImage(image=img) imageBlob = cv2.dnn.blobFromImage(image=current_img)
face_detector = self.model["face_detector"] face_detector = self.model["face_detector"]
face_detector.setInput(imageBlob) face_detector.setInput(imageBlob)
@ -126,17 +134,21 @@ class SsdClient(Detector):
bottom = instance["bottom"] bottom = instance["bottom"]
top = instance["top"] top = instance["top"]
detected_face = base_img[ x = int(left * aspect_ratio_x)
int(top * aspect_ratio_y) : int(bottom * aspect_ratio_y), y = int(top * aspect_ratio_y)
int(left * aspect_ratio_x) : int(right * aspect_ratio_x), w = int(right * aspect_ratio_x) - int(left * aspect_ratio_x)
] h = int(bottom * aspect_ratio_y) - int(top * aspect_ratio_y)
face_region = FacialAreaRegion( # expand the facial area to be extracted and stay within img.shape limits
x=int(left * aspect_ratio_x), x2 = max(0, x - int((w * expand_percentage) / 100)) # expand left
y=int(top * aspect_ratio_y), y2 = max(0, y - int((h * expand_percentage) / 100)) # expand top
w=int(right * aspect_ratio_x) - int(left * aspect_ratio_x), w2 = min(img.shape[1], w + int((w * expand_percentage) / 100)) # expand right
h=int(bottom * aspect_ratio_y) - int(top * aspect_ratio_y), h2 = min(img.shape[0], h + int((h * expand_percentage) / 100)) # expand bottom
)
detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
detected_face = img[int(y2) : int(y2 + h2), int(x2) : int(x2 + w2)]
face_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = instance["confidence"] confidence = instance["confidence"]

View File

@ -51,18 +51,27 @@ class YoloClient(Detector):
# Return face_detector # Return face_detector
return YOLO(weight_path) return YOLO(weight_path)
def detect_faces(self, img: np.ndarray, align: bool = False) -> List[DetectedFace]: def detect_faces(
self, img: np.ndarray, align: bool = False, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
Detect and align face with yolo Detect and align face with yolo
Args: Args:
face_detector (Any): yolo face detector object img (np.ndarray): pre-loaded image as numpy array
img (np.ndarray): pre-loaded image
align (bool): default is true align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[Tuple[DetectedFace]): A list of DetectedFace objects results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- img (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face. - confidence (float): The confidence score associated with the detected face.
""" """
resp = [] resp = []
@ -78,7 +87,15 @@ class YoloClient(Detector):
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) region = FacialAreaRegion(x=x, y=y, w=w, h=h)
detected_face = img[y : y + h, x : x + w].copy()
# expand the facial area to be extracted and stay within img.shape limits
x2 = max(0, x - int((w * expand_percentage) / 100)) # expand left
y2 = max(0, y - int((h * expand_percentage) / 100)) # expand top
w2 = min(img.shape[1], w + int((w * expand_percentage) / 100)) # expand right
h2 = min(img.shape[0], h + int((h * expand_percentage) / 100)) # expand bottom
# detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
detected_face = img[int(y2) : int(y2 + h2), int(x2) : int(x2 + w2)]
if align: if align:
# Tuple of x,y and confidence for left eye # Tuple of x,y and confidence for left eye

View File

@ -49,17 +49,27 @@ class YuNetClient(Detector):
) from err ) from err
return face_detector return face_detector
def detect_faces(self, img: np.ndarray, align: bool = True) -> List[DetectedFace]: def detect_faces(
self, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
""" """
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 as numpy array
align (bool): default is true
align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[DetectedFace]): A list of DetectedFace objects results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- img (np.ndarray): The detected face as a NumPy array. - img (np.ndarray): The detected face as a NumPy array.
- facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face. - confidence (float): The confidence score associated with the detected face.
""" """
# FaceDetector.detect_faces does not support score_threshold parameter. # FaceDetector.detect_faces does not support score_threshold parameter.
@ -115,7 +125,16 @@ 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)]
# expand the facial area to be extracted and stay within img.shape limits
x2 = max(0, x - int((w * expand_percentage) / 100)) # expand left
y2 = max(0, y - int((h * expand_percentage) / 100)) # expand top
w2 = min(img.shape[1], w + int((w * expand_percentage) / 100)) # expand right
h2 = min(img.shape[0], h + int((h * expand_percentage) / 100)) # expand bottom
# detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]
detected_face = img[int(y2) : int(y2 + h2), int(x2) : int(x2 + w2)]
img_region = FacialAreaRegion(x=x, y=y, w=w, h=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))

View File

@ -8,19 +8,28 @@ 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(self, img: np.ndarray, align: bool = True) -> List["DetectedFace"]: def detect_faces(
self, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List["DetectedFace"]:
""" """
Detect faces from a given image Interface for detect and align face
Args: Args:
img (np.ndarray): pre-loaded image as a NumPy array img (np.ndarray): pre-loaded image as numpy array
align (bool): enable or disable alignment after face detection
align (bool): flag to enable or disable alignment after detection (default is True)
expand_percentage (int): expand detected facial area with a percentage
Returns: Returns:
results (List[DetectedFace]): A list of DetectedFace object results (List[Tuple[DetectedFace]): A list of DetectedFace objects
where each object contains: where each object contains:
- face (np.ndarray): The detected face as a NumPy array.
- face_region (List[float]): The image region represented as - img (np.ndarray): The detected face as a NumPy array.
a list of floats e.g. [x, y, w, h]
- confidence (float): The confidence score associated with the detected face. - facial_area (FacialAreaRegion): The facial area region represented as x, y, w, h
- confidence (float): The confidence score associated with the detected face.
""" """
pass pass

View File

@ -16,6 +16,7 @@ def analyze(
enforce_detection: bool = True, enforce_detection: bool = True,
detector_backend: str = "opencv", detector_backend: str = "opencv",
align: bool = True, align: bool = True,
expand_percentage: int = 0,
silent: bool = False, silent: bool = False,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
""" """
@ -40,6 +41,8 @@ def analyze(
align (boolean): Perform alignment based on the eye positions (default is True). align (boolean): Perform alignment based on the eye positions (default is True).
expand_percentage (int): expand detected facial area with a percentage (default is 0).
silent (boolean): Suppress or allow some log messages for a quieter analysis process silent (boolean): Suppress or allow some log messages for a quieter analysis process
(default is False). (default is False).
@ -120,6 +123,7 @@ def analyze(
grayscale=False, grayscale=False,
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
align=align, align=align,
expand_percentage=expand_percentage,
) )
for img_obj in img_objs: for img_obj in img_objs:

View File

@ -31,6 +31,7 @@ def extract_faces(
detector_backend: str = "opencv", detector_backend: str = "opencv",
enforce_detection: bool = True, enforce_detection: bool = True,
align: bool = True, align: bool = True,
expand_percentage: int = 0,
grayscale: bool = False, grayscale: bool = False,
human_readable=False, human_readable=False,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
@ -52,6 +53,8 @@ def extract_faces(
align (bool): Flag to enable face alignment (default is True). 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 grayscale (boolean): Flag to convert the image to grayscale before
processing (default is False). processing (default is False).
@ -75,7 +78,12 @@ def extract_faces(
if detector_backend == "skip": if detector_backend == "skip":
face_objs = [DetectedFace(img=img, facial_area=base_region, confidence=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=detector_backend,
img=img,
align=align,
expand_percentage=expand_percentage,
)
# in case of no face found # in case of no face found
if len(face_objs) == 0 and enforce_detection is True: if len(face_objs) == 0 and enforce_detection is True:

View File

@ -26,6 +26,7 @@ def find(
enforce_detection: bool = True, enforce_detection: bool = True,
detector_backend: str = "opencv", detector_backend: str = "opencv",
align: bool = True, align: bool = True,
expand_percentage: int = 0,
threshold: Optional[float] = None, threshold: Optional[float] = None,
normalization: str = "base", normalization: str = "base",
silent: bool = False, silent: bool = False,
@ -55,6 +56,8 @@ def find(
align (boolean): Perform alignment based on the eye positions. align (boolean): Perform alignment based on the eye positions.
expand_percentage (int): expand detected facial area with a percentage (default is 0).
threshold (float): Specify a threshold to determine whether a pair represents the same threshold (float): Specify a threshold to determine whether a pair represents the same
person or different individuals. This threshold is used for comparing distances. person or different individuals. This threshold is used for comparing distances.
If left unset, default pre-tuned threshold values will be applied based on the specified If left unset, default pre-tuned threshold values will be applied based on the specified
@ -211,6 +214,7 @@ def find(
grayscale=False, grayscale=False,
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
align=align, align=align,
expand_percentage=expand_percentage,
) )
resp_obj = [] resp_obj = []
@ -309,6 +313,7 @@ def __find_bulk_embeddings(
detector_backend: str = "opencv", detector_backend: str = "opencv",
enforce_detection: bool = True, enforce_detection: bool = True,
align: bool = True, align: bool = True,
expand_percentage: int = 0,
normalization: str = "base", normalization: str = "base",
silent: bool = False, silent: bool = False,
): ):
@ -317,15 +322,24 @@ def __find_bulk_embeddings(
Args: Args:
employees (list): list of exact image paths employees (list): list of exact image paths
model_name (str): facial recognition model name model_name (str): facial recognition model name
target_size (tuple): expected input shape of facial
recognition model target_size (tuple): expected input shape of facial recognition model
detector_backend (str): face detector model name detector_backend (str): face detector model name
enforce_detection (bool): set this to False if you enforce_detection (bool): set this to False if you
want to proceed when you cannot detect any face want to proceed when you cannot detect any face
align (bool): enable or disable alignment of image align (bool): enable or disable alignment of image
before feeding to facial recognition model before feeding to facial recognition model
expand_percentage (int): expand detected facial area with a
percentage (default is 0).
normalization (bool): normalization technique normalization (bool): normalization technique
silent (bool): enable or disable informative logging silent (bool): enable or disable informative logging
Returns: Returns:
representations (list): pivot list of embeddings with representations (list): pivot list of embeddings with
@ -344,6 +358,7 @@ def __find_bulk_embeddings(
grayscale=False, grayscale=False,
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
align=align, align=align,
expand_percentage=expand_percentage,
) )
for img_obj in img_objs: for img_obj in img_objs:

View File

@ -16,6 +16,7 @@ def represent(
enforce_detection: bool = True, enforce_detection: bool = True,
detector_backend: str = "opencv", detector_backend: str = "opencv",
align: bool = True, align: bool = True,
expand_percentage: int = 0,
normalization: str = "base", normalization: str = "base",
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
""" """
@ -37,6 +38,8 @@ def represent(
align (boolean): Perform alignment based on the eye positions. align (boolean): Perform alignment based on the eye positions.
expand_percentage (int): expand detected facial area with a percentage (default is 0).
normalization (string): Normalize the input image before feeding it to the model. normalization (string): Normalize the input image before feeding it to the model.
Default is base. Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace Default is base. Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace
@ -69,6 +72,7 @@ def represent(
grayscale=False, grayscale=False,
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
align=align, align=align,
expand_percentage=expand_percentage,
) )
else: # skip else: # skip
# Try load. If load error, will raise exception internal # Try load. If load error, will raise exception internal

View File

@ -19,6 +19,7 @@ def verify(
distance_metric: str = "cosine", distance_metric: str = "cosine",
enforce_detection: bool = True, enforce_detection: bool = True,
align: bool = True, align: bool = True,
expand_percentage: int = 0,
normalization: str = "base", normalization: str = "base",
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
@ -49,6 +50,8 @@ def verify(
align (bool): Flag to enable face alignment (default is True). align (bool): Flag to enable face alignment (default is True).
expand_percentage (int): expand detected facial area with a percentage (default is 0).
normalization (string): Normalize the input image before feeding it to the model. normalization (string): Normalize the input image before feeding it to the model.
Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace (default is base) Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace (default is base)
@ -91,6 +94,7 @@ def verify(
grayscale=False, grayscale=False,
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
align=align, align=align,
expand_percentage=expand_percentage,
) )
img2_objs = detection.extract_faces( img2_objs = detection.extract_faces(
@ -100,6 +104,7 @@ def verify(
grayscale=False, grayscale=False,
enforce_detection=enforce_detection, enforce_detection=enforce_detection,
align=align, align=align,
expand_percentage=expand_percentage,
) )
# -------------------------------- # --------------------------------
distances = [] distances = []