Merge branch 'serengil:master' into distances

This commit is contained in:
Golubkov Alexander 2024-08-04 19:43:52 +03:00 committed by GitHub
commit 1d3de83c2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 130 additions and 117 deletions

View File

@ -25,7 +25,7 @@
DeepFace is a lightweight [face recognition](https://sefiks.com/2018/08/06/deep-face-recognition-with-keras/) and facial attribute analysis ([age](https://sefiks.com/2019/02/13/apparent-age-and-gender-prediction-in-keras/), [gender](https://sefiks.com/2019/02/13/apparent-age-and-gender-prediction-in-keras/), [emotion](https://sefiks.com/2018/01/01/facial-expression-recognition-with-keras/) and [race](https://sefiks.com/2019/11/11/race-and-ethnicity-prediction-in-keras/)) framework for python. It is a hybrid face recognition framework wrapping **state-of-the-art** models: [`VGG-Face`](https://sefiks.com/2018/08/06/deep-face-recognition-with-keras/), [`FaceNet`](https://sefiks.com/2018/09/03/face-recognition-with-facenet-in-keras/), [`OpenFace`](https://sefiks.com/2019/07/21/face-recognition-with-openface-in-keras/), [`DeepFace`](https://sefiks.com/2020/02/17/face-recognition-with-facebook-deepface-in-keras/), [`DeepID`](https://sefiks.com/2020/06/16/face-recognition-with-deepid-in-keras/), [`ArcFace`](https://sefiks.com/2020/12/14/deep-face-recognition-with-arcface-in-keras-and-python/), [`Dlib`](https://sefiks.com/2020/07/11/face-recognition-with-dlib-in-python/), `SFace` and `GhostFaceNet`.
[`Experiments`](https://github.com/serengil/deepface/tree/master/benchmarks) show that human beings have 97.53% accuracy on facial recognition tasks whereas those models already reached and passed that accuracy level.
[`Experiments`](https://github.com/serengil/deepface/tree/master/benchmarks) show that **human beings have 97.53% accuracy** on facial recognition tasks whereas those models already reached and passed that accuracy level.
## Installation [![PyPI](https://img.shields.io/pypi/v/deepface.svg)](https://pypi.org/project/deepface/)
@ -35,7 +35,7 @@ The easiest way to install deepface is to download it from [`PyPI`](https://pypi
$ pip install deepface
```
You can also install deepface from its source code.
Alternatively, you can also install deepface from its source code. Source code may have new features not published in pip release yet.
```shell
$ git clone https://github.com/serengil/deepface.git
@ -43,7 +43,7 @@ $ cd deepface
$ pip install -e .
```
Then you will be able to import the library and use its functionalities.
Once you installed the library, then you will be able to import it and use its functionalities.
```python
from deepface import DeepFace

View File

@ -48,17 +48,23 @@ if tf_version == 2:
folder_utils.initialize_folder()
def build_model(model_name: str) -> Any:
def build_model(model_name: str, task: str = "facial_recognition") -> Any:
"""
This function builds a deepface model
This function builds a pre-trained model
Args:
model_name (string): face recognition or facial attribute model
VGG-Face, Facenet, OpenFace, DeepFace, DeepID for face recognition
Age, Gender, Emotion, Race for facial attributes
model_name (str): model identifier
- VGG-Face, Facenet, Facenet512, OpenFace, DeepFace, DeepID, Dlib,
ArcFace, SFace, GhostFaceNet for face recognition
- Age, Gender, Emotion, Race for facial attributes
- opencv, mtcnn, ssd, dlib, retinaface, mediapipe, yolov8, yunet,
fastmtcnn or centerface for face detectors
- Fasnet for spoofing
task (str): facial_recognition, facial_attribute, face_detector, spoofing
default is facial_recognition
Returns:
built_model
"""
return modeling.build_model(model_name=model_name)
return modeling.build_model(task=task, model_name=model_name)
def verify(

View File

@ -1 +1 @@
__version__ = "0.0.92"
__version__ = "0.0.93"

View File

@ -1,64 +1,13 @@
from typing import Any, List, Tuple
from typing import List, Tuple
import numpy as np
import cv2
from deepface.modules import detection
from deepface.modules import detection, modeling
from deepface.models.Detector import Detector, DetectedFace, FacialAreaRegion
from deepface.detectors import (
FastMtCnn,
MediaPipe,
MtCnn,
OpenCv,
Dlib,
RetinaFace,
Ssd,
Yolo,
YuNet,
CenterFace,
)
from deepface.commons.logger import Logger
logger = Logger()
def build_model(detector_backend: str) -> Any:
"""
Build a face detector model
Args:
detector_backend (str): backend detector name
Returns:
built detector (Any)
"""
global face_detector_obj # singleton design pattern
backends = {
"opencv": OpenCv.OpenCvClient,
"mtcnn": MtCnn.MtCnnClient,
"ssd": Ssd.SsdClient,
"dlib": Dlib.DlibClient,
"retinaface": RetinaFace.RetinaFaceClient,
"mediapipe": MediaPipe.MediaPipeClient,
"yolov8": Yolo.YoloClient,
"yunet": YuNet.YuNetClient,
"fastmtcnn": FastMtCnn.FastMtCnnClient,
"centerface": CenterFace.CenterFaceClient,
}
if not "face_detector_obj" in globals():
face_detector_obj = {}
built_models = list(face_detector_obj.keys())
if detector_backend not in built_models:
face_detector = backends.get(detector_backend)
if face_detector:
face_detector = face_detector()
face_detector_obj[detector_backend] = face_detector
else:
raise ValueError("invalid detector_backend passed - " + detector_backend)
return face_detector_obj[detector_backend]
def detect_faces(
detector_backend: str, img: np.ndarray, align: bool = True, expand_percentage: int = 0
) -> List[DetectedFace]:
@ -87,7 +36,9 @@ def detect_faces(
"""
height, width, _ = img.shape
face_detector: Detector = build_model(detector_backend)
face_detector: Detector = modeling.build_model(
task="face_detector", model_name=detector_backend
)
# validate expand percentage score
if expand_percentage < 0:

View File

@ -33,7 +33,9 @@ class ApparentAgeClient(Demography):
self.model_name = "Age"
def predict(self, img: np.ndarray) -> np.float64:
age_predictions = self.model.predict(img, verbose=0)[0, :]
# model.predict causes memory issue when it is called in a for loop
# age_predictions = self.model.predict(img, verbose=0)[0, :]
age_predictions = self.model(img, training=False).numpy()[0, :]
return find_apparent_age(age_predictions)

View File

@ -52,7 +52,10 @@ class EmotionClient(Demography):
img_gray = cv2.resize(img_gray, (48, 48))
img_gray = np.expand_dims(img_gray, axis=0)
emotion_predictions = self.model.predict(img_gray, verbose=0)[0, :]
# model.predict causes memory issue when it is called in a for loop
# emotion_predictions = self.model.predict(img_gray, verbose=0)[0, :]
emotion_predictions = self.model(img_gray, training=False).numpy()[0, :]
return emotion_predictions

View File

@ -41,7 +41,9 @@ class GenderClient(Demography):
self.model_name = "Gender"
def predict(self, img: np.ndarray) -> np.ndarray:
return self.model.predict(img, verbose=0)[0, :]
# model.predict causes memory issue when it is called in a for loop
# return self.model.predict(img, verbose=0)[0, :]
return self.model(img, training=False).numpy()[0, :]
def load_model(

View File

@ -40,7 +40,9 @@ class RaceClient(Demography):
self.model_name = "Race"
def predict(self, img: np.ndarray) -> np.ndarray:
return self.model.predict(img, verbose=0)[0, :]
# model.predict causes memory issue when it is called in a for loop
# return self.model.predict(img, verbose=0)[0, :]
return self.model(img, training=False).numpy()[0, :]
def load_model(

View File

@ -158,7 +158,9 @@ def analyze(
pbar.set_description(f"Action: {action}")
if action == "emotion":
emotion_predictions = modeling.build_model("Emotion").predict(img_content)
emotion_predictions = modeling.build_model(
task="facial_attribute", model_name="Emotion"
).predict(img_content)
sum_of_predictions = emotion_predictions.sum()
obj["emotion"] = {}
@ -169,12 +171,16 @@ def analyze(
obj["dominant_emotion"] = Emotion.labels[np.argmax(emotion_predictions)]
elif action == "age":
apparent_age = modeling.build_model("Age").predict(img_content)
apparent_age = modeling.build_model(
task="facial_attribute", model_name="Age"
).predict(img_content)
# int cast is for exception - object of type 'float32' is not JSON serializable
obj["age"] = int(apparent_age)
elif action == "gender":
gender_predictions = modeling.build_model("Gender").predict(img_content)
gender_predictions = modeling.build_model(
task="facial_attribute", model_name="Gender"
).predict(img_content)
obj["gender"] = {}
for i, gender_label in enumerate(Gender.labels):
gender_prediction = 100 * gender_predictions[i]
@ -183,7 +189,9 @@ def analyze(
obj["dominant_gender"] = Gender.labels[np.argmax(gender_predictions)]
elif action == "race":
race_predictions = modeling.build_model("Race").predict(img_content)
race_predictions = modeling.build_model(
task="facial_attribute", model_name="Race"
).predict(img_content)
sum_of_predictions = race_predictions.sum()
obj["race"] = {}

View File

@ -25,7 +25,7 @@ def extract_faces(
align: bool = True,
expand_percentage: int = 0,
grayscale: bool = False,
color_face: str = 'rgb',
color_face: str = "rgb",
normalize_face: bool = True,
anti_spoofing: bool = False,
) -> List[Dict[str, Any]]:
@ -126,16 +126,14 @@ def extract_faces(
logger.warn("Parameter grayscale is deprecated. Use color_face instead.")
current_img = cv2.cvtColor(current_img, cv2.COLOR_BGR2GRAY)
else:
if color_face == 'rgb':
if color_face == "rgb":
current_img = current_img[:, :, ::-1]
elif color_face == 'bgr':
elif color_face == "bgr":
pass # image is in BGR
elif color_face == 'gray':
elif color_face == "gray":
current_img = cv2.cvtColor(current_img, cv2.COLOR_BGR2GRAY)
else:
raise ValueError(
f"The color_face can be rgb, bgr or gray, but it is {color_face}."
)
raise ValueError(f"The color_face can be rgb, bgr or gray, but it is {color_face}.")
if normalize_face:
current_img = current_img / 255 # normalize input in [0, 1]
@ -159,7 +157,7 @@ def extract_faces(
}
if anti_spoofing is True:
antispoof_model = modeling.build_model(model_name="Fasnet")
antispoof_model = modeling.build_model(task="spoofing", model_name="Fasnet")
is_real, antispoof_score = antispoof_model.analyze(img=img, facial_area=(x, y, w, h))
resp_obj["is_real"] = is_real
resp_obj["antispoof_score"] = antispoof_score

View File

@ -13,51 +13,88 @@ from deepface.basemodels import (
Facenet,
GhostFaceNet,
)
from deepface.detectors import (
FastMtCnn,
MediaPipe,
MtCnn,
OpenCv,
Dlib as DlibDetector,
RetinaFace,
Ssd,
Yolo,
YuNet,
CenterFace,
)
from deepface.extendedmodels import Age, Gender, Race, Emotion
from deepface.spoofmodels import FasNet
def build_model(model_name: str) -> Any:
def build_model(task: str, model_name: str) -> Any:
"""
This function builds a deepface model
This function loads a pre-trained models as singletonish way
Parameters:
model_name (string): face recognition or facial attribute model
VGG-Face, Facenet, OpenFace, DeepFace, DeepID for face recognition
Age, Gender, Emotion, Race for facial attributes
task (str): facial_recognition, facial_attribute, face_detector, spoofing
model_name (str): model identifier
- VGG-Face, Facenet, Facenet512, OpenFace, DeepFace, DeepID, Dlib,
ArcFace, SFace, GhostFaceNet for face recognition
- Age, Gender, Emotion, Race for facial attributes
- opencv, mtcnn, ssd, dlib, retinaface, mediapipe, yolov8, yunet,
fastmtcnn or centerface for face detectors
- Fasnet for spoofing
Returns:
built model class
"""
# singleton design pattern
global model_obj
global cached_models
models = {
"VGG-Face": VGGFace.VggFaceClient,
"OpenFace": OpenFace.OpenFaceClient,
"Facenet": Facenet.FaceNet128dClient,
"Facenet512": Facenet.FaceNet512dClient,
"DeepFace": FbDeepFace.DeepFaceClient,
"DeepID": DeepID.DeepIdClient,
"Dlib": Dlib.DlibClient,
"ArcFace": ArcFace.ArcFaceClient,
"SFace": SFace.SFaceClient,
"GhostFaceNet": GhostFaceNet.GhostFaceNetClient,
"Emotion": Emotion.EmotionClient,
"Age": Age.ApparentAgeClient,
"Gender": Gender.GenderClient,
"Race": Race.RaceClient,
"Fasnet": FasNet.Fasnet,
"facial_recognition": {
"VGG-Face": VGGFace.VggFaceClient,
"OpenFace": OpenFace.OpenFaceClient,
"Facenet": Facenet.FaceNet128dClient,
"Facenet512": Facenet.FaceNet512dClient,
"DeepFace": FbDeepFace.DeepFaceClient,
"DeepID": DeepID.DeepIdClient,
"Dlib": Dlib.DlibClient,
"ArcFace": ArcFace.ArcFaceClient,
"SFace": SFace.SFaceClient,
"GhostFaceNet": GhostFaceNet.GhostFaceNetClient,
},
"spoofing": {
"Fasnet": FasNet.Fasnet,
},
"facial_attribute": {
"Emotion": Emotion.EmotionClient,
"Age": Age.ApparentAgeClient,
"Gender": Gender.GenderClient,
"Race": Race.RaceClient,
},
"face_detector": {
"opencv": OpenCv.OpenCvClient,
"mtcnn": MtCnn.MtCnnClient,
"ssd": Ssd.SsdClient,
"dlib": DlibDetector.DlibClient,
"retinaface": RetinaFace.RetinaFaceClient,
"mediapipe": MediaPipe.MediaPipeClient,
"yolov8": Yolo.YoloClient,
"yunet": YuNet.YuNetClient,
"fastmtcnn": FastMtCnn.FastMtCnnClient,
"centerface": CenterFace.CenterFaceClient,
},
}
if not "model_obj" in globals():
model_obj = {}
if models.get(task) is None:
raise ValueError(f"unimplemented task - {task}")
if not model_name in model_obj.keys():
model = models.get(model_name)
if not "cached_models" in globals():
cached_models = {current_task: {} for current_task in models.keys()}
if cached_models[task].get(model_name) is None:
model = models[task].get(model_name)
if model:
model_obj[model_name] = model()
cached_models[task][model_name] = model()
else:
raise ValueError(f"Invalid model_name passed - {model_name}")
raise ValueError(f"Invalid model_name passed - {task}/{model_name}")
return model_obj[model_name]
return cached_models[task][model_name]

View File

@ -65,7 +65,9 @@ def represent(
"""
resp_objs = []
model: FacialRecognition = modeling.build_model(model_name)
model: FacialRecognition = modeling.build_model(
task="facial_recognition", model_name=model_name
)
# ---------------------------------
# we have run pre-process in verification. so, this can be skipped if it is coming from verify.

View File

@ -171,7 +171,7 @@ def build_facial_recognition_model(model_name: str) -> None:
Returns
input_shape (tuple): input shape of given facial recognitio n model.
"""
_ = DeepFace.build_model(model_name=model_name)
_ = DeepFace.build_model(task="facial_recognition", model_name=model_name)
logger.info(f"{model_name} is built")
@ -267,11 +267,11 @@ def build_demography_models(enable_face_analysis: bool) -> None:
"""
if enable_face_analysis is False:
return
DeepFace.build_model(model_name="Age")
DeepFace.build_model(task="facial_attribute", model_name="Age")
logger.info("Age model is just built")
DeepFace.build_model(model_name="Gender")
DeepFace.build_model(task="facial_attribute", model_name="Gender")
logger.info("Gender model is just built")
DeepFace.build_model(model_name="Emotion")
DeepFace.build_model(task="facial_attribute", model_name="Emotion")
logger.info("Emotion model is just built")

View File

@ -100,7 +100,9 @@ def verify(
tic = time.time()
model: FacialRecognition = modeling.build_model(model_name)
model: FacialRecognition = modeling.build_model(
task="facial_recognition", model_name=model_name
)
dims = model.output_shape
# extract faces from img1

View File

@ -1,3 +1,3 @@
{
"version": "0.0.92"
"version": "0.0.93"
}

View File

@ -16,7 +16,7 @@ logger = Logger()
model_name = "VGG-Face"
model: FacialRecognition = DeepFace.build_model(model_name=model_name)
model: FacialRecognition = DeepFace.build_model(task="facial_recognition", model_name=model_name)
target_size = model.input_shape