From 3471874b8fdca2ee837d9410973457272117012d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Eefik=20Serangil?= Date: Sun, 16 Aug 2020 21:24:12 +0300 Subject: [PATCH] dlib resnet added --- README.md | 4 +- api/api.py | 8 +++- deepface/DeepFace.py | 69 ++++++++++++++++++++----------- deepface/basemodels/DlibResNet.py | 62 +++++++++++++++++++++++++++ deepface/commons/functions.py | 8 ++++ deepface/commons/realtime.py | 6 +++ setup.py | 2 +- tests/unit_tests.py | 9 ++-- 8 files changed, 136 insertions(+), 32 deletions(-) create mode 100644 deepface/basemodels/DlibResNet.py diff --git a/README.md b/README.md index 083795e..1ad80f3 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,10 @@ df = DeepFace.find(img_path = "img1.jpg", db_path = "C:/workspace/my_db") **Face recognition models** -Deepface is a hybrid face recognition package. It currently wraps the **state-of-the-art** face recognition models: [`VGG-Face`](https://sefiks.com/2018/08/06/deep-face-recognition-with-keras/) , [`Google 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/), [`Facebook DeepFace`](https://sefiks.com/2020/02/17/face-recognition-with-facebook-deepface-in-keras/) and [`DeepID`](https://sefiks.com/2020/06/16/face-recognition-with-deepid-in-keras/). The default configuration verifies faces with **VGG-Face** model. You can set the base model while verification as illustared below. +Deepface is a hybrid face recognition package. It currently wraps the **state-of-the-art** face recognition models: [`VGG-Face`](https://sefiks.com/2018/08/06/deep-face-recognition-with-keras/) , [`Google 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/), [`Facebook 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/) and [`Dlib`](https://sefiks.com/2020/07/11/face-recognition-with-dlib-in-python/). The default configuration verifies faces with **VGG-Face** model. You can set the base model while verification as illustared below. ```python -models = ["VGG-Face", "Facenet", "OpenFace", "DeepFace", "DeepID"] +models = ["VGG-Face", "Facenet", "OpenFace", "DeepFace", "DeepID", "Dlib"] for model in models: result = DeepFace.verify("img1.jpg", "img2.jpg", model_name = model) ``` diff --git a/api/api.py b/api/api.py index 8b1229b..f7688d7 100644 --- a/api/api.py +++ b/api/api.py @@ -10,6 +10,7 @@ import tensorflow as tf from deepface import DeepFace from deepface.basemodels import VGGFace, OpenFace, Facenet, FbDeepFace, DeepID +from deepface.basemodels.DlibResNet import DlibResNet from deepface.extendedmodels import Age, Gender, Race, Emotion #import DeepFace @@ -26,7 +27,7 @@ tic = time.time() print("Loading Face Recognition Models...") -pbar = tqdm(range(0,5), desc='Loading Face Recognition Models...') +pbar = tqdm(range(0,6), desc='Loading Face Recognition Models...') for index in pbar: if index == 0: @@ -44,6 +45,9 @@ for index in pbar: elif index == 4: pbar.set_description("Loading DeepID DeepFace") deepid_model = DeepID.loadModel() + elif index == 5: + pbar.set_description("Loading Dlib ResNet DeepFace") + dlib_model = DlibResNet() toc = time.time() @@ -200,6 +204,8 @@ def verify(): resp_obj = DeepFace.verify(instances, model_name = model_name, distance_metric = distance_metric, model = deepface_model) elif model_name == "DeepID": resp_obj = DeepFace.verify(instances, model_name = model_name, distance_metric = distance_metric, model = deepid_model) + elif model_name == "Dlib": + resp_obj = DeepFace.verify(instances, model_name = model_name, distance_metric = distance_metric, model = dlib_model) elif model_name == "Ensemble": models = {} models["VGG-Face"] = vggface_model diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index f892ded..e2a6d93 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -18,6 +18,7 @@ import pickle from deepface import DeepFace from deepface.basemodels import VGGFace, OpenFace, Facenet, FbDeepFace, DeepID +from deepface.basemodels.DlibResNet import DlibResNet from deepface.extendedmodels import Age, Gender, Race, Emotion from deepface.commons import functions, realtime, distance as dst @@ -219,6 +220,10 @@ def verify(img1_path, img2_path='' elif model_name == 'DeepID': print("Using DeepID2 model backend", distance_metric,"distance.") model = DeepID.loadModel() + + elif model_name == 'Dlib': + print("Using Dlib ResNet model backend", distance_metric,"distance.") + model = DlibResNet() else: raise ValueError("Invalid model_name passed - ", model_name) @@ -227,15 +232,19 @@ def verify(img1_path, img2_path='' #------------------------------ #face recognition models have different size of inputs - #input_shape = model.layers[0].input_shape[1:3] #my environment returns (None, 224, 224, 3) but some people mentioned that they got [(None, 224, 224, 3)]. I think this is because of version issue. + #my environment returns (None, 224, 224, 3) but some people mentioned that they got [(None, 224, 224, 3)]. I think this is because of version issue. + + if model_name == 'Dlib': #this is not a regular keras model + input_shape = (150, 150, 3) - input_shape = model.layers[0].input_shape - - if type(input_shape) == list: - input_shape = input_shape[0][1:3] - else: - input_shape = input_shape[1:3] - + else: #keras based models + input_shape = model.layers[0].input_shape + + if type(input_shape) == list: + input_shape = input_shape[0][1:3] + else: + input_shape = input_shape[1:3] + input_shape_x = input_shape[0] input_shape_y = input_shape[1] @@ -536,8 +545,10 @@ def find(img_path, db_path elif model_name == 'DeepID': print("Using DeepID model backend", distance_metric,"distance.") model = DeepID.loadModel() + elif model_name == 'Dlib': + print("Using Dlib ResNet model backend", distance_metric,"distance.") + model = DlibResNet() elif model_name == 'Ensemble': - print("Ensemble learning enabled") #TODO: include DeepID in ensemble method @@ -622,15 +633,20 @@ def find(img_path, db_path if model_name != 'Ensemble': - #input_shape = model.layers[0].input_shape[1:3] #my environment returns (None, 224, 224, 3) but some people mentioned that they got [(None, 224, 224, 3)]. I think this is because of version issue. - - input_shape = model.layers[0].input_shape - - if type(input_shape) == list: - input_shape = input_shape[0][1:3] + if model_name == 'Dlib': #non-keras model + input_shape = (150, 150, 3) else: - input_shape = input_shape[1:3] - + #input_shape = model.layers[0].input_shape[1:3] #my environment returns (None, 224, 224, 3) but some people mentioned that they got [(None, 224, 224, 3)]. I think this is because of version issue. + + input_shape = model.layers[0].input_shape + + if type(input_shape) == list: + input_shape = input_shape[0][1:3] + else: + input_shape = input_shape[1:3] + + #--------------------- + input_shape_x = input_shape[0]; input_shape_y = input_shape[1] img = functions.detectFace(employee, (input_shape_y, input_shape_x), enforce_detection = enforce_detection) @@ -779,15 +795,20 @@ def find(img_path, db_path #---------------------------------- if model_name != 'Ensemble': - - #input_shape = model.layers[0].input_shape[1:3] #my environment returns (None, 224, 224, 3) but some people mentioned that they got [(None, 224, 224, 3)]. I think this is because of version issue. - - input_shape = model.layers[0].input_shape - if type(input_shape) == list: - input_shape = input_shape[0][1:3] + if model_name == 'Dlib': #non-keras model + input_shape = (150, 150, 3) else: - input_shape = input_shape[1:3] + #input_shape = model.layers[0].input_shape[1:3] #my environment returns (None, 224, 224, 3) but some people mentioned that they got [(None, 224, 224, 3)]. I think this is because of version issue. + + input_shape = model.layers[0].input_shape + + if type(input_shape) == list: + input_shape = input_shape[0][1:3] + else: + input_shape = input_shape[1:3] + + #------------------------ input_shape_x = input_shape[0]; input_shape_y = input_shape[1] diff --git a/deepface/basemodels/DlibResNet.py b/deepface/basemodels/DlibResNet.py new file mode 100644 index 0000000..ae00c2c --- /dev/null +++ b/deepface/basemodels/DlibResNet.py @@ -0,0 +1,62 @@ +import dlib +import os +import zipfile +import bz2 +import gdown +import numpy as np +from pathlib import Path + +class DlibResNet: + + def __init__(self): + + home = str(Path.home()) + weight_file = home+'/.deepface/weights/dlib_face_recognition_resnet_model_v1.dat' + + #--------------------- + + #download pre-trained model if it does not exist + if os.path.isfile(weight_file) != True: + print("dlib_face_recognition_resnet_model_v1.dat is going to be downloaded") + + url = "http://dlib.net/files/dlib_face_recognition_resnet_model_v1.dat.bz2" + output = home+'/.deepface/weights/'+url.split("/")[-1] + gdown.download(url, output, quiet=False) + + zipfile = bz2.BZ2File(output) + data = zipfile.read() + newfilepath = output[:-4] #discard .bz2 extension + open(newfilepath, 'wb').write(data) + + #--------------------- + + model = dlib.face_recognition_model_v1(weight_file) + self.__model = model + + #--------------------- + + return None #classes must return None + + def predict(self, img_aligned): + + #functions.detectFace returns 4 dimensional images + if len(img_aligned.shape) == 4: + img_aligned = img_aligned[0] + + #functions.detectFace returns bgr images + img_aligned = img_aligned[:,:,::-1] #bgr to rgb + + #deepface.detectFace returns an array in scale of [0, 1] but dlib expects in scale of [0, 255] + if img_aligned.max() <= 1: + img_aligned = img_aligned * 255 + + img_aligned = img_aligned.astype(np.uint8) + + model = self.__model + + img_representation = model.compute_face_descriptor(img_aligned) + + img_representation = np.array(img_representation) + img_representation = np.expand_dims(img_representation, axis = 0) + + return img_representation \ No newline at end of file diff --git a/deepface/commons/functions.py b/deepface/commons/functions.py index 1ec650c..621ee7f 100644 --- a/deepface/commons/functions.py +++ b/deepface/commons/functions.py @@ -137,6 +137,14 @@ def findThreshold(model_name, distance_metric): elif distance_metric == 'euclidean_l2': threshold = 0.17 + elif model_name == 'Dlib': + if distance_metric == 'cosine': + threshold = 0.07 + elif distance_metric == 'euclidean': + threshold = 0.60 + elif distance_metric == 'euclidean_l2': + threshold = 0.60 + return threshold def get_opencv_path(): diff --git a/deepface/commons/realtime.py b/deepface/commons/realtime.py index ac8f4ab..c0f7412 100644 --- a/deepface/commons/realtime.py +++ b/deepface/commons/realtime.py @@ -10,6 +10,7 @@ import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' from deepface.basemodels import VGGFace, OpenFace, Facenet, FbDeepFace, DeepID +from deepface.basemodels.DlibResNet import DlibResNet from deepface.extendedmodels import Age, Gender, Race, Emotion from deepface.commons import functions, realtime, distance as dst @@ -58,6 +59,11 @@ def analysis(db_path, model_name, distance_metric, enable_face_analysis = True): model = DeepID.loadModel() input_shape = (55, 47) + elif model_name == 'Dlib': + print("Using Dlib model backend", distance_metric,"distance.") + model = DlibResNet() + input_shape = (150, 150) + else: raise ValueError("Invalid model_name passed - ", model_name) #------------------------ diff --git a/setup.py b/setup.py index 6cb0bc6..cc2b367 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh: setuptools.setup( name="deepface", - version="0.0.33", + version="0.0.34", 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", diff --git a/tests/unit_tests.py b/tests/unit_tests.py index ff0058a..0928aa6 100644 --- a/tests/unit_tests.py +++ b/tests/unit_tests.py @@ -11,7 +11,9 @@ print("-----------------------------------------") print("Large scale face recognition") -df = DeepFace.find(img_path = "dataset/img1.jpg", db_path = "dataset") +df = DeepFace.find(img_path = "dataset/img1.jpg", db_path = "dataset" + #, model_name = 'Dlib' +) print(df.head()) print("-----------------------------------------") @@ -105,7 +107,7 @@ dataset = [ ['dataset/img6.jpg', 'dataset/img9.jpg', False], ] -models = ['VGG-Face', 'Facenet', 'OpenFace', 'DeepFace', 'DeepID'] +models = ['VGG-Face', 'Facenet', 'OpenFace', 'DeepFace', 'DeepID', 'Dlib'] metrics = ['cosine', 'euclidean', 'euclidean_l2'] passed_tests = 0; test_cases = 0 @@ -134,7 +136,7 @@ for model in models: test_cases = test_cases + 1 - print(img1, " and ", img2," are ", classified_label, " as same person based on ", model," model and ",metric," distance metric. Distance: ",distance,", Required Threshold: ", required_threshold," (",test_result_label,")") + print(img1.split("/")[-1], "and", img2.split("/")[-1],"are", classified_label, "as same person based on", model,"model and",metric,"distance. Distance:",distance,", Threshold:", required_threshold,"(",test_result_label,")") print("--------------------------") @@ -178,4 +180,3 @@ facial_attribute_models["gender"] = gender_model facial_attribute_models["race"] = race_model resp_obj = DeepFace.analyze("dataset/img1.jpg", models=facial_attribute_models) -