Skip to content

Commit 36665a9

Browse files
committed
january 26 improvements
1 parent 88814e6 commit 36665a9

File tree

7 files changed

+109
-21
lines changed

7 files changed

+109
-21
lines changed

deepface/DeepFace.py

+77-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import warnings
44
import logging
5-
from typing import Any, Dict, List, Tuple, Union
5+
from typing import Any, Dict, List, Tuple, Union, Optional
66

77
# 3rd party dependencies
88
import numpy as np
@@ -65,34 +65,49 @@ def verify(
6565
Args:
6666
img1_path (str or np.ndarray): Path to the first image. Accepts exact image path
6767
as a string, numpy array (BGR), or base64 encoded images.
68+
6869
img2_path (str or np.ndarray): Path to the second image. Accepts exact image path
6970
as a string, numpy array (BGR), or base64 encoded images.
71+
7072
model_name (str): Model for face recognition. Options: VGG-Face, Facenet, Facenet512,
7173
OpenFace, DeepFace, DeepID, Dlib, ArcFace and SFace (default is VGG-Face).
74+
7275
detector_backend (string): face detector backend. Options: 'opencv', 'retinaface',
73-
'mtcnn', 'ssd', 'dlib', 'mediapipe', 'yolov8' (default is opencv)
76+
'mtcnn', 'ssd', 'dlib', 'mediapipe', 'yolov8' (default is opencv).
77+
7478
distance_metric (string): Metric for measuring similarity. Options: 'cosine',
7579
'euclidean', 'euclidean_l2' (default is cosine).
80+
7681
enforce_detection (boolean): If no face is detected in an image, raise an exception.
7782
Set to False to avoid the exception for low-resolution images (default is True).
83+
7884
align (bool): Flag to enable face alignment (default is True).
85+
7986
normalization (string): Normalize the input image before feeding it to the model.
8087
Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace (default is base)
88+
8189
Returns:
8290
result (dict): A dictionary containing verification results with following keys.
91+
8392
- 'verified' (bool): Indicates whether the images represent the same person (True)
8493
or different persons (False).
94+
8595
- 'distance' (float): The distance measure between the face vectors.
8696
A lower distance indicates higher similarity.
97+
8798
- 'max_threshold_to_verify' (float): The maximum threshold used for verification.
8899
If the distance is below this threshold, the images are considered a match.
100+
89101
- 'model' (str): The chosen face recognition model.
102+
90103
- 'similarity_metric' (str): The chosen similarity metric for measuring distances.
104+
91105
- 'facial_areas' (dict): Rectangular regions of interest for faces in both images.
92106
- 'img1': {'x': int, 'y': int, 'w': int, 'h': int}
93107
Region of interest for the first image.
94108
- 'img2': {'x': int, 'y': int, 'w': int, 'h': int}
95109
Region of interest for the second image.
110+
96111
- 'time' (float): Time taken for the verification process in seconds.
97112
"""
98113

@@ -122,37 +137,51 @@ def analyze(
122137
img_path (str or np.ndarray): The exact path to the image, a numpy array in BGR format,
123138
or a base64 encoded image. If the source image contains multiple faces, the result will
124139
include information for each detected face.
140+
125141
actions (tuple): Attributes to analyze. The default is ('age', 'gender', 'emotion', 'race').
126142
You can exclude some of these attributes from the analysis if needed.
143+
127144
enforce_detection (boolean): If no face is detected in an image, raise an exception.
128145
Set to False to avoid the exception for low-resolution images (default is True).
146+
129147
detector_backend (string): face detector backend. Options: 'opencv', 'retinaface',
130148
'mtcnn', 'ssd', 'dlib', 'mediapipe', 'yolov8' (default is opencv).
149+
131150
distance_metric (string): Metric for measuring similarity. Options: 'cosine',
132151
'euclidean', 'euclidean_l2' (default is cosine).
152+
133153
align (boolean): Perform alignment based on the eye positions (default is True).
154+
134155
silent (boolean): Suppress or allow some log messages for a quieter analysis process
135156
(default is False).
157+
136158
Returns:
137159
results (List[Dict[str, Any]]): A list of dictionaries, where each dictionary represents
138160
the analysis results for a detected face. Each dictionary in the list contains the
139161
following keys:
162+
140163
- 'region' (dict): Represents the rectangular region of the detected face in the image.
141164
- 'x': x-coordinate of the top-left corner of the face.
142165
- 'y': y-coordinate of the top-left corner of the face.
143166
- 'w': Width of the detected face region.
144167
- 'h': Height of the detected face region.
168+
145169
- 'age' (float): Estimated age of the detected face.
170+
146171
- 'face_confidence' (float): Confidence score for the detected face.
147172
Indicates the reliability of the face detection.
173+
148174
- 'dominant_gender' (str): The dominant gender in the detected face.
149-
Either "Man" or "Woman."
175+
Either "Man" or "Woman".
176+
150177
- 'gender' (dict): Confidence scores for each gender category.
151178
- 'Man': Confidence score for the male gender.
152179
- 'Woman': Confidence score for the female gender.
180+
153181
- 'dominant_emotion' (str): The dominant emotion in the detected face.
154182
Possible values include "sad," "angry," "surprise," "fear," "happy,"
155-
"disgust," and "neutral."
183+
"disgust," and "neutral"
184+
156185
- 'emotion' (dict): Confidence scores for each emotion category.
157186
- 'sad': Confidence score for sadness.
158187
- 'angry': Confidence score for anger.
@@ -161,9 +190,11 @@ def analyze(
161190
- 'happy': Confidence score for happiness.
162191
- 'disgust': Confidence score for disgust.
163192
- 'neutral': Confidence score for neutrality.
193+
164194
- 'dominant_race' (str): The dominant race in the detected face.
165195
Possible values include "indian," "asian," "latino hispanic,"
166196
"black," "middle eastern," and "white."
197+
167198
- 'race' (dict): Confidence scores for each race category.
168199
- 'indian': Confidence score for Indian ethnicity.
169200
- 'asian': Confidence score for Asian ethnicity.
@@ -190,6 +221,7 @@ def find(
190221
enforce_detection: bool = True,
191222
detector_backend: str = "opencv",
192223
align: bool = True,
224+
threshold: Optional[float] = None,
193225
normalization: str = "base",
194226
silent: bool = False,
195227
) -> List[pd.DataFrame]:
@@ -199,31 +231,51 @@ def find(
199231
img_path (str or np.ndarray): The exact path to the image, a numpy array in BGR format,
200232
or a base64 encoded image. If the source image contains multiple faces, the result will
201233
include information for each detected face.
234+
202235
db_path (string): Path to the folder containing image files. All detected faces
203236
in the database will be considered in the decision-making process.
237+
204238
model_name (str): Model for face recognition. Options: VGG-Face, Facenet, Facenet512,
205239
OpenFace, DeepFace, DeepID, Dlib, ArcFace and SFace (default is VGG-Face).
240+
206241
distance_metric (string): Metric for measuring similarity. Options: 'cosine',
207242
'euclidean', 'euclidean_l2' (default is cosine).
243+
208244
enforce_detection (boolean): If no face is detected in an image, raise an exception.
209245
Set to False to avoid the exception for low-resolution images (default is True).
246+
210247
detector_backend (string): face detector backend. Options: 'opencv', 'retinaface',
211248
'mtcnn', 'ssd', 'dlib', 'mediapipe', 'yolov8' (default is opencv).
249+
212250
align (boolean): Perform alignment based on the eye positions (default is True).
251+
252+
threshold (float): Specify a threshold to determine whether a pair represents the same
253+
person or different individuals. This threshold is used for comparing distances.
254+
If left unset, default pre-tuned threshold values will be applied based on the specified
255+
model name and distance metric (default is None).
256+
213257
normalization (string): Normalize the input image before feeding it to the model.
214258
Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace (default is base).
259+
215260
silent (boolean): Suppress or allow some log messages for a quieter analysis process
216261
(default is False).
262+
217263
Returns:
218264
results (List[pd.DataFrame]): A list of pandas dataframes. Each dataframe corresponds
219265
to the identity information for an individual detected in the source image.
220266
The DataFrame columns include:
267+
221268
- 'identity': Identity label of the detected individual.
269+
222270
- 'target_x', 'target_y', 'target_w', 'target_h': Bounding box coordinates of the
223271
target face in the database.
272+
224273
- 'source_x', 'source_y', 'source_w', 'source_h': Bounding box coordinates of the
225274
detected face in the source image.
226-
- '{model_name}_{distance_metric}': Similarity score between the faces based on the
275+
276+
- 'threshold': threshold to determine a pair whether same person or different persons
277+
278+
- 'distance': Similarity score between the faces based on the
227279
specified model and distance metric
228280
"""
229281
return recognition.find(
@@ -234,6 +286,7 @@ def find(
234286
enforce_detection=enforce_detection,
235287
detector_backend=detector_backend,
236288
align=align,
289+
threshold=threshold,
237290
normalization=normalization,
238291
silent=silent,
239292
)
@@ -254,27 +307,36 @@ def represent(
254307
img_path (str or np.ndarray): The exact path to the image, a numpy array in BGR format,
255308
or a base64 encoded image. If the source image contains multiple faces, the result will
256309
include information for each detected face.
310+
257311
model_name (str): Model for face recognition. Options: VGG-Face, Facenet, Facenet512,
258312
OpenFace, DeepFace, DeepID, Dlib, ArcFace and SFace (default is VGG-Face.).
313+
259314
enforce_detection (boolean): If no face is detected in an image, raise an exception.
260315
Default is True. Set to False to avoid the exception for low-resolution images
261316
(default is True).
317+
262318
detector_backend (string): face detector backend. Options: 'opencv', 'retinaface',
263319
'mtcnn', 'ssd', 'dlib', 'mediapipe', 'yolov8' (default is opencv).
320+
264321
align (boolean): Perform alignment based on the eye positions (default is True).
322+
265323
normalization (string): Normalize the input image before feeding it to the model.
266324
Default is base. Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace
267325
(default is base).
326+
268327
Returns:
269328
results (List[Dict[str, Any]]): A list of dictionaries, each containing the
270329
following fields:
330+
271331
- embedding (np.array): Multidimensional vector representing facial features.
272332
The number of dimensions varies based on the reference model
273333
(e.g., FaceNet returns 128 dimensions, VGG-Face returns 4096 dimensions).
334+
274335
- facial_area (dict): Detected facial area by face detection in dictionary format.
275336
Contains 'x' and 'y' as the left-corner point, and 'w' and 'h'
276337
as the width and height. If `detector_backend` is set to 'skip', it represents
277338
the full image area and is nonsensical.
339+
278340
- face_confidence (float): Confidence score of face detection. If `detector_backend` is set
279341
to 'skip', the confidence will be 0 and is nonsensical.
280342
"""
@@ -355,19 +417,28 @@ def extract_faces(
355417
Args:
356418
img_path (str or np.ndarray): Path to the first image. Accepts exact image path
357419
as a string, numpy array (BGR), or base64 encoded images.
420+
358421
target_size (tuple): final shape of facial image. black pixels will be
359422
added to resize the image (default is (224, 224)).
423+
360424
detector_backend (string): face detector backend. Options: 'opencv', 'retinaface',
361-
'mtcnn', 'ssd', 'dlib', 'mediapipe', 'yolov8' (default is opencv)
425+
'mtcnn', 'ssd', 'dlib', 'mediapipe', 'yolov8' (default is opencv).
426+
362427
enforce_detection (boolean): If no face is detected in an image, raise an exception.
363428
Set to False to avoid the exception for low-resolution images (default is True).
429+
364430
align (bool): Flag to enable face alignment (default is True).
431+
365432
grayscale (boolean): Flag to convert the image to grayscale before
366433
processing (default is False).
434+
367435
Returns:
368436
results (List[Dict[str, Any]]): A list of dictionaries, where each dictionary contains:
437+
369438
- "face" (np.ndarray): The detected face as a NumPy array.
439+
370440
- "facial_area" (List[float]): The detected face's regions represented as a list of floats.
441+
371442
- "confidence" (float): The confidence score associated with the detected face.
372443
"""
373444

deepface/detectors/Ssd.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
logger = Logger(module="detectors.SsdWrapper")
1414

15-
# pylint: disable=line-too-long
15+
# pylint: disable=line-too-long, c-extension-no-member
1616

1717

1818
class SsdClient(Detector):

deepface/detectors/YuNet.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ def build_model(self) -> Any:
2121
Returns:
2222
model (Any)
2323
"""
24+
25+
opencv_version = cv2.__version__.split(".")
26+
27+
if len(opencv_version) > 2 and int(opencv_version[0]) == 4 and int(opencv_version[1]) < 8:
28+
# min requirement: https://github.com/opencv/opencv_zoo/issues/172
29+
raise ValueError(f"YuNet requires opencv-python >= 4.8 but you have {cv2.__version__}")
30+
2431
# pylint: disable=C0301
2532
url = "https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx"
2633
file_name = "face_detection_yunet_2023mar.onnx"
@@ -67,7 +74,7 @@ def detect_faces(
6774
"""
6875
# FaceDetector.detect_faces does not support score_threshold parameter.
6976
# We can set it via environment variable.
70-
score_threshold = os.environ.get("yunet_score_threshold", "0.9")
77+
score_threshold = float(os.environ.get("yunet_score_threshold", "0.9"))
7178
resp = []
7279
detected_face = None
7380
img_region = [0, 0, img.shape[1], img.shape[0]]

deepface/modules/recognition.py

+18-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# built-in dependencies
22
import os
33
import pickle
4-
from typing import List, Union
4+
from typing import List, Union, Optional
55
import time
66

77
# 3rd party dependencies
@@ -25,6 +25,7 @@ def find(
2525
enforce_detection: bool = True,
2626
detector_backend: str = "opencv",
2727
align: bool = True,
28+
threshold: Optional[float] = None,
2829
normalization: str = "base",
2930
silent: bool = False,
3031
) -> List[pd.DataFrame]:
@@ -53,6 +54,11 @@ def find(
5354
5455
align (boolean): Perform alignment based on the eye positions.
5556
57+
threshold (float): Specify a threshold to determine whether a pair represents the same
58+
person or different individuals. This threshold is used for comparing distances.
59+
If left unset, default pre-tuned threshold values will be applied based on the specified
60+
model name and distance metric (default is None).
61+
5662
normalization (string): Normalize the input image before feeding it to the model.
5763
Default is base. Options: base, raw, Facenet, Facenet2018, VGGFace, VGGFace2, ArcFace
5864
@@ -64,11 +70,16 @@ def find(
6470
The DataFrame columns include:
6571
6672
- 'identity': Identity label of the detected individual.
73+
6774
- 'target_x', 'target_y', 'target_w', 'target_h': Bounding box coordinates of the
6875
target face in the database.
76+
6977
- 'source_x', 'source_y', 'source_w', 'source_h': Bounding box coordinates of the
7078
detected face in the source image.
71-
- '{model_name}_{distance_metric}': Similarity score between the faces based on the
79+
80+
- 'threshold': threshold to determine a pair whether same person or different persons
81+
82+
- 'distance': Similarity score between the faces based on the
7283
specified model and distance metric
7384
"""
7485

@@ -248,16 +259,15 @@ def find(
248259
distances.append(distance)
249260

250261
# ---------------------------
262+
target_threshold = threshold or dst.findThreshold(model_name, distance_metric)
251263

252-
result_df[f"{model_name}_{distance_metric}"] = distances
264+
result_df["threshold"] = target_threshold
265+
result_df["distance"] = distances
253266

254-
threshold = dst.findThreshold(model_name, distance_metric)
255267
result_df = result_df.drop(columns=[f"{model_name}_representation"])
256268
# pylint: disable=unsubscriptable-object
257-
result_df = result_df[result_df[f"{model_name}_{distance_metric}"] <= threshold]
258-
result_df = result_df.sort_values(
259-
by=[f"{model_name}_{distance_metric}"], ascending=True
260-
).reset_index(drop=True)
269+
result_df = result_df[result_df["distance"] <= target_threshold]
270+
result_df = result_df.sort_values(by=["distance"], ascending=True).reset_index(drop=True)
261271

262272
resp_obj.append(result_df)
263273

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
setuptools.setup(
1010
name="deepface",
11-
version="0.0.82",
11+
version="0.0.83",
1212
author="Sefik Ilkin Serengil",
1313
author_email="[email protected]",
1414
description="A Lightweight Face Recognition and Facial Attribute Analysis Framework (Age, Gender, Emotion, Race) for Python",

tests/test_find.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def test_find_with_exact_path():
2121
assert identity_df.shape[0] > 0
2222

2323
# validate reproducability
24-
assert identity_df["VGG-Face_cosine"].values[0] < threshold
24+
assert identity_df["distance"].values[0] < threshold
2525

2626
df = df[df["identity"] != img_path]
2727
logger.debug(df.head())
@@ -42,7 +42,7 @@ def test_find_with_array_input():
4242
assert identity_df.shape[0] > 0
4343

4444
# validate reproducability
45-
assert identity_df["VGG-Face_cosine"].values[0] < threshold
45+
assert identity_df["distance"].values[0] < threshold
4646

4747
df = df[df["identity"] != img_path]
4848
logger.debug(df.head())
@@ -65,7 +65,7 @@ def test_find_with_extracted_faces():
6565
assert identity_df.shape[0] > 0
6666

6767
# validate reproducability
68-
assert identity_df["VGG-Face_cosine"].values[0] < threshold
68+
assert identity_df["distance"].values[0] < threshold
6969

7070
df = df[df["identity"] != img_path]
7171
logger.debug(df.head())

0 commit comments

Comments
 (0)