open issues

This commit is contained in:
Sefik Ilkin Serengil 2024-02-02 17:33:31 +00:00
parent 64d33717a9
commit 8497682171
42 changed files with 326 additions and 224 deletions

41
CITATION.md Normal file
View File

@ -0,0 +1,41 @@
## Cite DeepFace Papers
Please cite deepface in your publications if it helps your research. Here are its BibTex entries:
### Facial Recognition
If you use deepface in your research for facial recogntion purposes, please cite the this publication.
```BibTeX
@inproceedings{serengil2020lightface,
title = {LightFace: A Hybrid Deep Face Recognition Framework},
author = {Serengil, Sefik Ilkin and Ozpinar, Alper},
booktitle = {2020 Innovations in Intelligent Systems and Applications Conference (ASYU)},
pages = {23-27},
year = {2020},
doi = {10.1109/ASYU50717.2020.9259802},
url = {https://doi.org/10.1109/ASYU50717.2020.9259802},
organization = {IEEE}
}
```
### Facial Attribute Analysis
If you use deepface in your research for facial attribute analysis purposes such as age, gender, emotion or ethnicity prediction or face detection purposes, please cite the this publication.
```BibTeX
@inproceedings{serengil2021lightface,
title = {HyperExtended LightFace: A Facial Attribute Analysis Framework},
author = {Serengil, Sefik Ilkin and Ozpinar, Alper},
booktitle = {2021 International Conference on Engineering and Emerging Technologies (ICEET)},
pages = {1-4},
year = {2021},
doi = {10.1109/ICEET53442.2021.9659697},
url = {https://doi.org/10.1109/ICEET53442.2021.9659697},
organization = {IEEE}
}
```
### Repositories
Also, if you use deepface in your GitHub projects, please add `deepface` in the `requirements.txt`. Thereafter, your project will be listed in its [dependency graph](https://github.com/serengil/deepface/network/dependents).

View File

@ -19,10 +19,10 @@ RUN apt-get install ffmpeg libsm6 libxext6 -y
# -----------------------------------
# Copy required files from repo into image
COPY ./deepface /app/deepface
COPY ./api/app.py /app/
COPY ./api/api.py /app/
COPY ./api/routes.py /app/
COPY ./api/service.py /app/
COPY ./api/src/app.py /app/
COPY ./api/src/api.py /app/
COPY ./api/src/routes.py /app/
COPY ./api/src/service.py /app/
COPY ./requirements.txt /app/
COPY ./setup.py /app/
COPY ./README.md /app/

View File

@ -289,7 +289,7 @@ cd scripts
<p align="center"><img src="https://raw.githubusercontent.com/serengil/deepface/master/icon/deepface-api.jpg" width="90%" height="90%"></p>
Face recognition, facial attribute analysis and vector representation functions are covered in the API. You are expected to call these functions as http post methods. Default service endpoints will be `http://localhost:5000/verify` for face recognition, `http://localhost:5000/analyze` for facial attribute analysis, and `http://localhost:5000/represent` for vector representation. You can pass input images as exact image paths on your environment, base64 encoded strings or images on web. [Here](https://github.com/serengil/deepface/tree/master/api), you can find a postman project to find out how these methods should be called.
Face recognition, facial attribute analysis and vector representation functions are covered in the API. You are expected to call these functions as http post methods. Default service endpoints will be `http://localhost:5000/verify` for face recognition, `http://localhost:5000/analyze` for facial attribute analysis, and `http://localhost:5000/represent` for vector representation. You can pass input images as exact image paths on your environment, base64 encoded strings or images on web. [Here](https://github.com/serengil/deepface/tree/master/api/postman), you can find a postman project to find out how these methods should be called.
**Dockerized Service**
@ -332,9 +332,9 @@ You can also support this work on [Patreon](https://www.patreon.com/serengil?rep
## Citation
Please cite deepface in your publications if it helps your research. Here are its BibTex entries:
Please cite deepface in your publications if it helps your research - see [`CITATIONS`](https://github.com/serengil/deepface/blob/master/CITATIONS.md) for more details. Here are its BibTex entries:
If you use deepface for facial recogntion purposes, please cite the this publication.
If you use deepface in your research for facial recogntion purposes, please cite this publication.
```BibTeX
@inproceedings{serengil2020lightface,
@ -349,7 +349,7 @@ If you use deepface for facial recogntion purposes, please cite the this publica
}
```
If you use deepface for facial attribute analysis purposes such as age, gender, emotion or ethnicity prediction or face detection purposes, please cite the this publication.
If you use deepface in your research for facial attribute analysis purposes such as age, gender, emotion or ethnicity prediction or face detection purposes, please cite this publication.
```BibTeX
@inproceedings{serengil2021lightface,

View File

@ -10,7 +10,7 @@ import pandas as pd
import tensorflow as tf
# package dependencies
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.modules import (
modeling,
@ -24,17 +24,21 @@ from deepface.modules import (
logger = Logger(module="DeepFace")
# current package version of deepface
__version__ = package_utils.find_package_version()
# -----------------------------------
# configurations for dependencies
warnings.filterwarnings("ignore")
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 2:
tf.get_logger().setLevel(logging.ERROR)
# -----------------------------------
functions.initialize_folder()
# create required folders if necessary to store model weights
folder_utils.initialize_folder()
def build_model(model_name: str) -> Any:
@ -511,7 +515,7 @@ def detectFace(
align (bool): Flag to enable face alignment (default is True).
Returns:
img (np.ndarray): detected (and aligned) facial area image as numpy array
img (np.ndarray): detected (and aligned) facial area image as numpy array
"""
logger.warn("Function detectFace is deprecated. Use extract_faces instead.")
face_objs = extract_faces(

View File

@ -2,7 +2,7 @@ from typing import List
import os
import gdown
import numpy as np
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.models.FacialRecognition import FacialRecognition
@ -13,7 +13,7 @@ logger = Logger(module="basemodels.ArcFace")
# --------------------------------
# dependency configuration
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model
@ -94,7 +94,7 @@ def load_model(
# ---------------------------------------
# check the availability of pre-trained weights
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
file_name = "arcface_weights.h5"
output = home + "/.deepface/weights/" + file_name

View File

@ -2,13 +2,13 @@ from typing import List
import os
import gdown
import numpy as np
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.models.FacialRecognition import FacialRecognition
logger = Logger(module="basemodels.DeepID")
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model
@ -100,7 +100,7 @@ def load_model(
# ---------------------------------
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + "/.deepface/weights/deepid_keras_weights.h5") != True:
logger.info("deepid_keras_weights.h5 will be downloaded...")

View File

@ -3,7 +3,7 @@ import os
import bz2
import gdown
import numpy as np
from deepface.commons import functions
from deepface.commons import folder_utils
from deepface.commons.logger import Logger
from deepface.models.FacialRecognition import FacialRecognition
@ -68,7 +68,7 @@ class DlibResNet:
# ---------------------
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
weight_file = home + "/.deepface/weights/dlib_face_recognition_resnet_model_v1.dat"
# ---------------------

View File

@ -2,7 +2,7 @@ from typing import List
import os
import gdown
import numpy as np
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.models.FacialRecognition import FacialRecognition
@ -11,7 +11,7 @@ logger = Logger(module="basemodels.Facenet")
# --------------------------------
# dependency configuration
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model
@ -1689,7 +1689,7 @@ def load_facenet128d_model(
# -----------------------------------
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + "/.deepface/weights/facenet_weights.h5") != True:
logger.info("facenet_weights.h5 will be downloaded...")
@ -1719,7 +1719,7 @@ def load_facenet512d_model(
# -------------------------
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + "/.deepface/weights/facenet512_weights.h5") != True:
logger.info("facenet512_weights.h5 will be downloaded...")

View File

@ -3,7 +3,7 @@ import os
import zipfile
import gdown
import numpy as np
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.models.FacialRecognition import FacialRecognition
@ -12,7 +12,7 @@ logger = Logger(module="basemodels.FbDeepFace")
# --------------------------------
# dependency configuration
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model, Sequential
@ -84,7 +84,7 @@ def load_model(
# ---------------------------------
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + "/.deepface/weights/VGGFace2_DeepFace_weights_val-0.9034.h5") != True:
logger.info("VGGFace2_DeepFace_weights_val-0.9034.h5 will be downloaded...")

View File

@ -3,13 +3,13 @@ import os
import gdown
import tensorflow as tf
import numpy as np
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.models.FacialRecognition import FacialRecognition
logger = Logger(module="basemodels.OpenFace")
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model
from keras.layers import Conv2D, ZeroPadding2D, Input, concatenate
@ -394,7 +394,7 @@ def load_model(
# -----------------------------------
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + "/.deepface/weights/openface_weights.h5") != True:
logger.info("openface_weights.h5 will be downloaded...")

View File

@ -5,7 +5,7 @@ import numpy as np
import cv2 as cv
import gdown
from deepface.commons import functions
from deepface.commons import folder_utils
from deepface.commons.logger import Logger
from deepface.models.FacialRecognition import FacialRecognition
@ -50,7 +50,7 @@ def load_model(
Construct SFace model, download its weights and load
"""
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
file_name = home + "/.deepface/weights/face_recognition_sface_2021dec.onnx"

View File

@ -2,7 +2,8 @@ from typing import List
import os
import gdown
import numpy as np
from deepface.commons import functions, distance
from deepface.commons import package_utils, folder_utils
from deepface.modules import verification
from deepface.models.FacialRecognition import FacialRecognition
from deepface.commons.logger import Logger
@ -10,7 +11,7 @@ logger = Logger(module="basemodels.VGGFace")
# ---------------------------------------
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model, Sequential
from keras.layers import (
@ -59,7 +60,7 @@ class VggFaceClient(FacialRecognition):
# having normalization layer in descriptor troubles for some gpu users (e.g. issue 957, 966)
# instead we are now calculating it with traditional way not with keras backend
embedding = self.model(img, training=False).numpy()[0].tolist()
embedding = distance.l2_normalize(embedding)
embedding = verification.l2_normalize(embedding)
return embedding.tolist()
@ -128,7 +129,7 @@ def load_model(
model = base_model()
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
output = home + "/.deepface/weights/vgg_face_weights.h5"
if os.path.isfile(output) != True:

View File

@ -0,0 +1,4 @@
import os
SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ROOT_DIR = os.path.dirname(SRC_DIR)

View File

@ -1,64 +0,0 @@
from typing import Union
import numpy as np
def find_cosine_distance(
source_representation: Union[np.ndarray, list], test_representation: Union[np.ndarray, list]
) -> np.float64:
if isinstance(source_representation, list):
source_representation = np.array(source_representation)
if isinstance(test_representation, list):
test_representation = np.array(test_representation)
a = np.matmul(np.transpose(source_representation), test_representation)
b = np.sum(np.multiply(source_representation, source_representation))
c = np.sum(np.multiply(test_representation, test_representation))
return 1 - (a / (np.sqrt(b) * np.sqrt(c)))
def find_euclidean_distance(
source_representation: Union[np.ndarray, list], test_representation: Union[np.ndarray, list]
) -> np.float64:
if isinstance(source_representation, list):
source_representation = np.array(source_representation)
if isinstance(test_representation, list):
test_representation = np.array(test_representation)
euclidean_distance = source_representation - test_representation
euclidean_distance = np.sum(np.multiply(euclidean_distance, euclidean_distance))
euclidean_distance = np.sqrt(euclidean_distance)
return euclidean_distance
def l2_normalize(x: Union[np.ndarray, list]) -> np.ndarray:
if isinstance(x, list):
x = np.array(x)
return x / np.sqrt(np.sum(np.multiply(x, x)))
def find_threshold(model_name: str, distance_metric: str) -> float:
base_threshold = {"cosine": 0.40, "euclidean": 0.55, "euclidean_l2": 0.75}
thresholds = {
# "VGG-Face": {"cosine": 0.40, "euclidean": 0.60, "euclidean_l2": 0.86}, # 2622d
"VGG-Face": {
"cosine": 0.68,
"euclidean": 1.17,
"euclidean_l2": 1.17,
}, # 4096d - tuned with LFW
"Facenet": {"cosine": 0.40, "euclidean": 10, "euclidean_l2": 0.80},
"Facenet512": {"cosine": 0.30, "euclidean": 23.56, "euclidean_l2": 1.04},
"ArcFace": {"cosine": 0.68, "euclidean": 4.15, "euclidean_l2": 1.13},
"Dlib": {"cosine": 0.07, "euclidean": 0.6, "euclidean_l2": 0.4},
"SFace": {"cosine": 0.593, "euclidean": 10.734, "euclidean_l2": 1.055},
"OpenFace": {"cosine": 0.10, "euclidean": 0.55, "euclidean_l2": 0.55},
"DeepFace": {"cosine": 0.23, "euclidean": 64, "euclidean_l2": 0.64},
"DeepID": {"cosine": 0.015, "euclidean": 45, "euclidean_l2": 0.17},
}
threshold = thresholds.get(model_name, base_threshold).get(distance_metric, 0.4)
return threshold

View File

@ -0,0 +1,35 @@
import os
from pathlib import Path
from deepface.commons.logger import Logger
logger = Logger(module="deepface/commons/folder_utils.py")
def initialize_folder() -> None:
"""
Initialize the folder for storing model weights.
Raises:
OSError: if the folder cannot be created.
"""
home = get_deepface_home()
deepface_home_path = home + "/.deepface"
weights_path = deepface_home_path + "/weights"
if not os.path.exists(deepface_home_path):
os.makedirs(deepface_home_path, exist_ok=True)
logger.info(f"Directory {home}/.deepface created")
if not os.path.exists(weights_path):
os.makedirs(weights_path, exist_ok=True)
logger.info(f"Directory {home}/.deepface/weights created")
def get_deepface_home() -> str:
"""
Get the home directory for storing model weights
Returns:
str: the home directory.
"""
return str(os.getenv("DEEPFACE_HOME", default=str(Path.home())))

View File

@ -1,42 +0,0 @@
import os
from pathlib import Path
# 3rd party dependencies
import tensorflow as tf
# package dependencies
from deepface.commons.logger import Logger
logger = Logger(module="commons.functions")
def get_tf_major_version() -> int:
return int(tf.__version__.split(".", maxsplit=1)[0])
def initialize_folder() -> None:
"""Initialize the folder for storing weights and models.
Raises:
OSError: if the folder cannot be created.
"""
home = get_deepface_home()
deepFaceHomePath = home + "/.deepface"
weightsPath = deepFaceHomePath + "/weights"
if not os.path.exists(deepFaceHomePath):
os.makedirs(deepFaceHomePath, exist_ok=True)
logger.info(f"Directory {home}/.deepface created")
if not os.path.exists(weightsPath):
os.makedirs(weightsPath, exist_ok=True)
logger.info(f"Directory {home}/.deepface/weights created")
def get_deepface_home() -> str:
"""Get the home directory for storing weights and models.
Returns:
str: the home directory.
"""
return str(os.getenv("DEEPFACE_HOME", default=str(Path.home())))

View File

@ -0,0 +1,36 @@
import json
# 3rd party dependencies
import tensorflow as tf
# package dependencies
from deepface.commons.logger import Logger
from deepface.commons import constant
logger = Logger(module="commons.package_utils")
def get_tf_major_version() -> int:
"""
Find tensorflow's major version
Returns
major_version (int)
"""
return int(tf.__version__.split(".", maxsplit=1)[0])
def find_package_version() -> str:
"""
Find the currenct package version
Returns:
version (str)
"""
version_info = "N/A"
try:
with open(f"{constant.ROOT_DIR}/package_info.json", "r", encoding="utf-8") as f:
package_info = json.load(f)
version_info = package_info["version"]
except Exception as err: # pylint: disable=broad-except
logger.error(f"Exception while getting version info: {str(err)}")
return version_info

View File

@ -3,7 +3,7 @@ import os
import bz2
import gdown
import numpy as np
from deepface.commons import functions
from deepface.commons import folder_utils
from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.commons.logger import Logger
@ -20,7 +20,7 @@ class DlibClient(Detector):
Returns:
model (Any)
"""
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
# this is not a must dependency. do not import it in the global level.
try:

View File

@ -36,45 +36,47 @@ class RetinaFaceClient(Detector):
obj = rf.detect_faces(img, model=self.model, threshold=0.9)
if isinstance(obj, dict):
for face_idx in obj.keys():
identity = obj[face_idx]
facial_area = identity["facial_area"]
if not isinstance(obj, dict):
return resp
y = facial_area[1]
h = facial_area[3] - y
x = facial_area[0]
w = facial_area[2] - x
img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = identity["score"]
for face_idx in obj.keys():
identity = obj[face_idx]
facial_area = identity["facial_area"]
# 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
y = facial_area[1]
h = facial_area[3] - y
x = facial_area[0]
w = facial_area[2] - x
img_region = FacialAreaRegion(x=x, y=y, w=w, h=h)
confidence = identity["score"]
# 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)]
# 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
if align:
landmarks = identity["landmarks"]
left_eye = landmarks["left_eye"]
right_eye = landmarks["right_eye"]
nose = landmarks["nose"]
# mouth_right = landmarks["mouth_right"]
# mouth_left = landmarks["mouth_left"]
# 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)]
detected_face = postprocess.alignment_procedure(
detected_face, right_eye, left_eye, nose
)
if align:
landmarks = identity["landmarks"]
left_eye = landmarks["left_eye"]
right_eye = landmarks["right_eye"]
nose = landmarks["nose"]
# mouth_right = landmarks["mouth_right"]
# mouth_left = landmarks["mouth_left"]
detected_face_obj = DetectedFace(
img=detected_face,
facial_area=img_region,
confidence=confidence,
detected_face = postprocess.alignment_procedure(
detected_face, right_eye, left_eye, nose
)
resp.append(detected_face_obj)
detected_face_obj = DetectedFace(
img=detected_face,
facial_area=img_region,
confidence=confidence,
)
resp.append(detected_face_obj)
return resp

View File

@ -5,7 +5,7 @@ import cv2
import pandas as pd
import numpy as np
from deepface.detectors import OpenCv
from deepface.commons import functions
from deepface.commons import folder_utils
from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection
from deepface.commons.logger import Logger
@ -26,7 +26,7 @@ class SsdClient(Detector):
model (dict)
"""
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
# model structure
if os.path.isfile(home + "/.deepface/weights/deploy.prototxt") != True:

View File

@ -4,6 +4,7 @@ import numpy as np
import gdown
from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection
from deepface.commons import folder_utils
from deepface.commons.logger import Logger
logger = Logger()
@ -39,9 +40,7 @@ class YoloClient(Detector):
Please install using 'pip install ultralytics' "
) from e
from deepface.commons.functions import get_deepface_home
weight_path = f"{get_deepface_home()}{PATH}"
weight_path = f"{folder_utils.get_deepface_home()}{PATH}"
# Download the model's weights if they don't exist
if not os.path.isfile(weight_path):

View File

@ -3,7 +3,7 @@ from typing import Any, List
import cv2
import numpy as np
import gdown
from deepface.commons import functions
from deepface.commons import folder_utils
from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.modules import detection
from deepface.commons.logger import Logger
@ -31,7 +31,7 @@ class YuNetClient(Detector):
# pylint: disable=C0301
url = "https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx"
file_name = "face_detection_yunet_2023mar.onnx"
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + f"/.deepface/weights/{file_name}") is False:
logger.info(f"{file_name} will be downloaded...")
output = home + f"/.deepface/weights/{file_name}"

View File

@ -2,7 +2,7 @@ import os
import gdown
import numpy as np
from deepface.basemodels import VGGFace
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.models.Demography import Demography
@ -11,7 +11,7 @@ logger = Logger(module="extendedmodels.Age")
# ----------------------------------------
# dependency configurations
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model, Sequential
@ -64,7 +64,7 @@ def load_model(
# load weights
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + "/.deepface/weights/age_model_weights.h5") != True:
logger.info("age_model_weights.h5 will be downloaded...")

View File

@ -2,7 +2,7 @@ import os
import gdown
import numpy as np
import cv2
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.models.Demography import Demography
@ -12,7 +12,7 @@ logger = Logger(module="extendedmodels.Emotion")
# pylint: disable=line-too-long
# -------------------------------------------
# dependency configuration
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Sequential
@ -88,7 +88,7 @@ def load_model(
# ----------------------------
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + "/.deepface/weights/facial_expression_model_weights.h5") != True:
logger.info("facial_expression_model_weights.h5 will be downloaded...")

View File

@ -2,7 +2,7 @@ import os
import gdown
import numpy as np
from deepface.basemodels import VGGFace
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.models.Demography import Demography
@ -13,7 +13,7 @@ logger = Logger(module="extendedmodels.Gender")
# -------------------------------------
# dependency configurations
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model, Sequential
from keras.layers import Convolution2D, Flatten, Activation
@ -66,7 +66,7 @@ def load_model(
# load weights
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + "/.deepface/weights/gender_model_weights.h5") != True:
logger.info("gender_model_weights.h5 will be downloaded...")

View File

@ -2,7 +2,7 @@ import os
import gdown
import numpy as np
from deepface.basemodels import VGGFace
from deepface.commons import functions
from deepface.commons import package_utils, folder_utils
from deepface.commons.logger import Logger
from deepface.models.Demography import Demography
@ -12,7 +12,7 @@ logger = Logger(module="extendedmodels.Race")
# pylint: disable=line-too-long
# --------------------------
# dependency configurations
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model, Sequential
@ -63,7 +63,7 @@ def load_model(
# load weights
home = functions.get_deepface_home()
home = folder_utils.get_deepface_home()
if os.path.isfile(home + "/.deepface/weights/race_model_single_batch.h5") != True:
logger.info("race_model_single_batch.h5 will be downloaded...")

View File

@ -1,9 +1,9 @@
from typing import Union
from abc import ABC, abstractmethod
import numpy as np
from deepface.commons import functions
from deepface.commons import package_utils
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model
else:

View File

@ -1,9 +1,9 @@
from abc import ABC, abstractmethod
from typing import Any, Union, List, Tuple
import numpy as np
from deepface.commons import functions
from deepface.commons import package_utils
tf_version = functions.get_tf_major_version()
tf_version = package_utils.get_tf_major_version()
if tf_version == 2:
from tensorflow.keras.models import Model
else:

View File

@ -10,7 +10,7 @@ from PIL import Image
from deepface.modules import preprocessing
from deepface.models.Detector import DetectedFace, FacialAreaRegion
from deepface.detectors import DetectorWrapper
from deepface.commons import functions
from deepface.commons import package_utils
from deepface.commons.logger import Logger
logger = Logger(module="deepface/modules/detection.py")
@ -18,7 +18,7 @@ logger = Logger(module="deepface/modules/detection.py")
# pylint: disable=no-else-raise
tf_major_version = functions.get_tf_major_version()
tf_major_version = package_utils.get_tf_major_version()
if tf_major_version == 1:
from keras.preprocessing import image
elif tf_major_version == 2:
@ -63,8 +63,11 @@ def extract_faces(
Returns:
results (List[Dict[str, Any]]): A list of dictionaries, where each dictionary contains:
- "face" (np.ndarray): The detected face as a NumPy array.
- "facial_area" (List[float]): The detected face's regions represented as a list of floats.
- "confidence" (float): The confidence score associated with the detected face.
"""

View File

@ -10,9 +10,8 @@ import pandas as pd
from tqdm import tqdm
# project dependencies
from deepface.commons import distance as dst
from deepface.commons.logger import Logger
from deepface.modules import representation, detection, modeling
from deepface.modules import representation, detection, modeling, verification
from deepface.models.FacialRecognition import FacialRecognition
logger = Logger(module="deepface/modules/recognition.py")
@ -253,13 +252,17 @@ def find(
)
if distance_metric == "cosine":
distance = dst.find_cosine_distance(source_representation, target_representation)
distance = verification.find_cosine_distance(
source_representation, target_representation
)
elif distance_metric == "euclidean":
distance = dst.find_euclidean_distance(source_representation, target_representation)
distance = verification.find_euclidean_distance(
source_representation, target_representation
)
elif distance_metric == "euclidean_l2":
distance = dst.find_euclidean_distance(
dst.l2_normalize(source_representation),
dst.l2_normalize(target_representation),
distance = verification.find_euclidean_distance(
verification.l2_normalize(source_representation),
verification.l2_normalize(target_representation),
)
else:
raise ValueError(f"invalid distance metric passes - {distance_metric}")
@ -267,7 +270,7 @@ def find(
distances.append(distance)
# ---------------------------
target_threshold = threshold or dst.find_threshold(model_name, distance_metric)
target_threshold = threshold or verification.find_threshold(model_name, distance_metric)
result_df["threshold"] = target_threshold
result_df["distance"] = distances

View File

@ -6,7 +6,6 @@ from typing import Any, Dict, Union
import numpy as np
# project dependencies
from deepface.commons import distance as dst
from deepface.modules import representation, detection, modeling
from deepface.models.FacialRecognition import FacialRecognition
@ -138,12 +137,12 @@ def verify(
img2_representation = img2_embedding_obj[0]["embedding"]
if distance_metric == "cosine":
distance = dst.find_cosine_distance(img1_representation, img2_representation)
distance = find_cosine_distance(img1_representation, img2_representation)
elif distance_metric == "euclidean":
distance = dst.find_euclidean_distance(img1_representation, img2_representation)
distance = find_euclidean_distance(img1_representation, img2_representation)
elif distance_metric == "euclidean_l2":
distance = dst.find_euclidean_distance(
dst.l2_normalize(img1_representation), dst.l2_normalize(img2_representation)
distance = find_euclidean_distance(
l2_normalize(img1_representation), l2_normalize(img2_representation)
)
else:
raise ValueError("Invalid distance_metric passed - ", distance_metric)
@ -152,7 +151,7 @@ def verify(
regions.append((img1_region, img2_region))
# -------------------------------
threshold = dst.find_threshold(model_name, distance_metric)
threshold = find_threshold(model_name, distance_metric)
distance = min(distances) # best distance
facial_areas = regions[np.argmin(distances)]
@ -171,3 +170,65 @@ def verify(
}
return resp_obj
def find_cosine_distance(
source_representation: Union[np.ndarray, list], test_representation: Union[np.ndarray, list]
) -> np.float64:
if isinstance(source_representation, list):
source_representation = np.array(source_representation)
if isinstance(test_representation, list):
test_representation = np.array(test_representation)
a = np.matmul(np.transpose(source_representation), test_representation)
b = np.sum(np.multiply(source_representation, source_representation))
c = np.sum(np.multiply(test_representation, test_representation))
return 1 - (a / (np.sqrt(b) * np.sqrt(c)))
def find_euclidean_distance(
source_representation: Union[np.ndarray, list], test_representation: Union[np.ndarray, list]
) -> np.float64:
if isinstance(source_representation, list):
source_representation = np.array(source_representation)
if isinstance(test_representation, list):
test_representation = np.array(test_representation)
euclidean_distance = source_representation - test_representation
euclidean_distance = np.sum(np.multiply(euclidean_distance, euclidean_distance))
euclidean_distance = np.sqrt(euclidean_distance)
return euclidean_distance
def l2_normalize(x: Union[np.ndarray, list]) -> np.ndarray:
if isinstance(x, list):
x = np.array(x)
return x / np.sqrt(np.sum(np.multiply(x, x)))
def find_threshold(model_name: str, distance_metric: str) -> float:
base_threshold = {"cosine": 0.40, "euclidean": 0.55, "euclidean_l2": 0.75}
thresholds = {
# "VGG-Face": {"cosine": 0.40, "euclidean": 0.60, "euclidean_l2": 0.86}, # 2622d
"VGG-Face": {
"cosine": 0.68,
"euclidean": 1.17,
"euclidean_l2": 1.17,
}, # 4096d - tuned with LFW
"Facenet": {"cosine": 0.40, "euclidean": 10, "euclidean_l2": 0.80},
"Facenet512": {"cosine": 0.30, "euclidean": 23.56, "euclidean_l2": 1.04},
"ArcFace": {"cosine": 0.68, "euclidean": 4.15, "euclidean_l2": 1.13},
"Dlib": {"cosine": 0.07, "euclidean": 0.6, "euclidean_l2": 0.4},
"SFace": {"cosine": 0.593, "euclidean": 10.734, "euclidean_l2": 1.055},
"OpenFace": {"cosine": 0.10, "euclidean": 0.55, "euclidean_l2": 0.55},
"DeepFace": {"cosine": 0.23, "euclidean": 64, "euclidean_l2": 0.64},
"DeepID": {"cosine": 0.015, "euclidean": 45, "euclidean_l2": 0.17},
}
threshold = thresholds.get(model_name, base_threshold).get(distance_metric, 0.4)
return threshold

3
package_info.json Normal file
View File

@ -0,0 +1,3 @@
{
"version": "0.0.84"
}

2
scripts/service.sh Normal file → Executable file
View File

@ -1,3 +1,3 @@
#!/usr/bin/env bash
cd ../api
cd ../api/src
gunicorn --workers=1 --timeout=3600 --bind=0.0.0.0:5000 "app:create_app()"

View File

@ -1,3 +1,4 @@
import json
import setuptools
with open("README.md", "r", encoding="utf-8") as fh:
@ -6,13 +7,19 @@ with open("README.md", "r", encoding="utf-8") as fh:
with open("requirements.txt", "r", encoding="utf-8") as f:
requirements = f.read().split("\n")
with open("package_info.json", "r", encoding="utf-8") as f:
package_info = json.load(f)
setuptools.setup(
name="deepface",
version="0.0.84",
version=package_info["version"],
author="Sefik Ilkin Serengil",
author_email="serengil@gmail.com",
description="A Lightweight Face Recognition and Facial Attribute Analysis Framework (Age, Gender, Emotion, Race) for Python",
data_files=[("", ["README.md", "requirements.txt"])],
description=(
"A Lightweight Face Recognition and Facial Attribute Analysis Framework"
" (Age, Gender, Emotion, Race) for Python"
),
data_files=[("", ["README.md", "requirements.txt", "package_info.json"])],
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/serengil/deepface",

View File

@ -1,7 +1,7 @@
import matplotlib.pyplot as plt
import numpy as np
from deepface import DeepFace
from deepface.commons import distance
from deepface.modules import verification
from deepface.models.FacialRecognition import FacialRecognition
from deepface.commons.logger import Logger
@ -38,7 +38,7 @@ distance_vector = np.square(img1_representation - img2_representation)
current_distance = np.sqrt(distance_vector.sum())
logger.info(f"Euclidean distance: {current_distance}")
threshold = distance.find_threshold(model_name=model_name, distance_metric="euclidean")
threshold = verification.find_threshold(model_name=model_name, distance_metric="euclidean")
logger.info(f"Threshold for {model_name}-euclidean pair is {threshold}")
if current_distance < threshold:

View File

@ -1,12 +1,12 @@
import cv2
import pandas as pd
from deepface import DeepFace
from deepface.commons import distance
from deepface.modules import verification
from deepface.commons.logger import Logger
logger = Logger("tests/test_find.py")
threshold = distance.find_threshold(model_name="VGG-Face", distance_metric="cosine")
threshold = verification.find_threshold(model_name="VGG-Face", distance_metric="cosine")
def test_find_with_exact_path():

View File

@ -20,7 +20,16 @@ model_names = [
"SFace",
]
detector_backends = ["opencv", "ssd", "dlib", "mtcnn", "retinaface", "yunet", "yolov8"]
detector_backends = [
"opencv",
"ssd",
"dlib",
"mtcnn",
# "mediapipe", # crashed in mac
"retinaface",
"yunet",
"yolov8",
]
# verification
for model_name in model_names: