represent function

This commit is contained in:
Sefik Ilkin Serengil 2021-04-10 12:30:24 +03:00
parent d51e628fb1
commit d43aacb87e
2 changed files with 290 additions and 242 deletions

View File

@ -13,20 +13,20 @@ from deepface.extendedmodels import Age, Gender, Race, Emotion
from deepface.commons import functions, realtime, distance as dst from deepface.commons import functions, realtime, distance as dst
def build_model(model_name): def build_model(model_name):
""" """
This function builds a deepface model This function builds a deepface model
Parameters: Parameters:
model_name (string): face recognition or facial attribute model model_name (string): face recognition or facial attribute model
VGG-Face, Facenet, OpenFace, DeepFace, DeepID for face recognition VGG-Face, Facenet, OpenFace, DeepFace, DeepID for face recognition
Age, Gender, Emotion, Race for facial attributes Age, Gender, Emotion, Race for facial attributes
Returns: Returns:
built deepface model built deepface model
""" """
models = { models = {
'VGG-Face': VGGFace.loadModel, 'VGG-Face': VGGFace.loadModel,
'OpenFace': OpenFace.loadModel, 'OpenFace': OpenFace.loadModel,
'Facenet': Facenet.loadModel, 'Facenet': Facenet.loadModel,
'DeepFace': FbDeepFace.loadModel, 'DeepFace': FbDeepFace.loadModel,
@ -40,7 +40,7 @@ def build_model(model_name):
} }
model = models.get(model_name) model = models.get(model_name)
if model: if model:
model = model() model = model()
#print('Using {} model backend'.format(model_name)) #print('Using {} model backend'.format(model_name))
@ -48,35 +48,34 @@ def build_model(model_name):
else: else:
raise ValueError('Invalid model_name passed - {}'.format(model_name)) raise ValueError('Invalid model_name passed - {}'.format(model_name))
def verify(img1_path, img2_path = '', model_name = 'VGG-Face', distance_metric = 'cosine', def verify(img1_path, img2_path = '', model_name = 'VGG-Face', distance_metric = 'cosine', model = None, enforce_detection = True, detector_backend = 'mtcnn'):
model = None, enforce_detection = True, detector_backend = 'mtcnn'):
""" """
This function verifies an image pair is same person or different persons. This function verifies an image pair is same person or different persons.
Parameters: Parameters:
img1_path, img2_path: exact image path, numpy array or based64 encoded images could be passed. If you are going to call verify function for a list of image pairs, then you should pass an array instead of calling the function in for loops. img1_path, img2_path: exact image path, numpy array or based64 encoded images could be passed. If you are going to call verify function for a list of image pairs, then you should pass an array instead of calling the function in for loops.
e.g. img1_path = [ e.g. img1_path = [
['img1.jpg', 'img2.jpg'], ['img1.jpg', 'img2.jpg'],
['img2.jpg', 'img3.jpg'] ['img2.jpg', 'img3.jpg']
] ]
model_name (string): VGG-Face, Facenet, OpenFace, DeepFace, DeepID, Dlib, ArcFace or Ensemble model_name (string): VGG-Face, Facenet, OpenFace, DeepFace, DeepID, Dlib, ArcFace or Ensemble
distance_metric (string): cosine, euclidean, euclidean_l2 distance_metric (string): cosine, euclidean, euclidean_l2
model: Built deepface model. A face recognition model is built every call of verify function. You can pass pre-built face recognition model optionally if you will call verify function several times. model: Built deepface model. A face recognition model is built every call of verify function. You can pass pre-built face recognition model optionally if you will call verify function several times.
model = DeepFace.build_model('VGG-Face') model = DeepFace.build_model('VGG-Face')
enforce_detection (boolean): If any face could not be detected in an image, then verify function will return exception. Set this to False not to have this exception. This might be convenient for low resolution images. enforce_detection (boolean): If any face could not be detected in an image, then verify function will return exception. Set this to False not to have this exception. This might be convenient for low resolution images.
detector_backend (string): set face detector backend as mtcnn, opencv, ssd or dlib detector_backend (string): set face detector backend as mtcnn, opencv, ssd or dlib
Returns: Returns:
Verify function returns a dictionary. If img1_path is a list of image pairs, then the function will return list of dictionary. Verify function returns a dictionary. If img1_path is a list of image pairs, then the function will return list of dictionary.
{ {
"verified": True "verified": True
, "distance": 0.2563 , "distance": 0.2563
@ -84,18 +83,18 @@ def verify(img1_path, img2_path = '', model_name = 'VGG-Face', distance_metric =
, "model": "VGG-Face" , "model": "VGG-Face"
, "similarity_metric": "cosine" , "similarity_metric": "cosine"
} }
""" """
tic = time.time() tic = time.time()
img_list, bulkProcess = functions.initialize_input(img1_path, img2_path) img_list, bulkProcess = functions.initialize_input(img1_path, img2_path)
functions.initialize_detector(detector_backend = detector_backend) functions.initialize_detector(detector_backend = detector_backend)
resp_objects = [] resp_objects = []
#-------------------------------- #--------------------------------
if model_name == 'Ensemble': if model_name == 'Ensemble':
model_names = ["VGG-Face", "Facenet", "OpenFace", "DeepFace"] model_names = ["VGG-Face", "Facenet", "OpenFace", "DeepFace"]
metrics = ["cosine", "euclidean", "euclidean_l2"] metrics = ["cosine", "euclidean", "euclidean_l2"]
@ -103,9 +102,9 @@ def verify(img1_path, img2_path = '', model_name = 'VGG-Face', distance_metric =
model_names = []; metrics = [] model_names = []; metrics = []
model_names.append(model_name) model_names.append(model_name)
metrics.append(distance_metric) metrics.append(distance_metric)
#-------------------------------- #--------------------------------
if model == None: if model == None:
if model_name == 'Ensemble': if model_name == 'Ensemble':
models = Boosting.loadModel() models = Boosting.loadModel()
@ -120,54 +119,54 @@ def verify(img1_path, img2_path = '', model_name = 'VGG-Face', distance_metric =
else: else:
models = {} models = {}
models[model_name] = model models[model_name] = model
#------------------------------ #------------------------------
#calling deepface in a for loop causes lots of progress bars. this prevents it. #calling deepface in a for loop causes lots of progress bars. this prevents it.
disable_option = False if len(img_list) > 1 else True disable_option = False if len(img_list) > 1 else True
pbar = tqdm(range(0,len(img_list)), desc='Verification', disable = disable_option) pbar = tqdm(range(0,len(img_list)), desc='Verification', disable = disable_option)
for index in pbar: for index in pbar:
instance = img_list[index] instance = img_list[index]
if type(instance) == list and len(instance) >= 2: if type(instance) == list and len(instance) >= 2:
img1_path = instance[0]; img2_path = instance[1] img1_path = instance[0]; img2_path = instance[1]
ensemble_features = [] ensemble_features = []
for i in model_names: for i in model_names:
custom_model = models[i] custom_model = models[i]
#decide input shape #decide input shape
input_shape = functions.find_input_shape(custom_model) input_shape = functions.find_input_shape(custom_model)
input_shape_x = input_shape[0]; input_shape_y = input_shape[1] input_shape_x = input_shape[0]; input_shape_y = input_shape[1]
#---------------------- #----------------------
#detect and align faces #detect and align faces
img1 = functions.preprocess_face(img=img1_path img1 = functions.preprocess_face(img=img1_path
, target_size=(input_shape_y, input_shape_x) , target_size=(input_shape_y, input_shape_x)
, enforce_detection = enforce_detection , enforce_detection = enforce_detection
, detector_backend = detector_backend) , detector_backend = detector_backend)
img2 = functions.preprocess_face(img=img2_path img2 = functions.preprocess_face(img=img2_path
, target_size=(input_shape_y, input_shape_x) , target_size=(input_shape_y, input_shape_x)
, enforce_detection = enforce_detection , enforce_detection = enforce_detection
, detector_backend = detector_backend) , detector_backend = detector_backend)
#---------------------- #----------------------
#find embeddings #find embeddings
img1_representation = custom_model.predict(img1)[0,:] img1_representation = custom_model.predict(img1)[0,:]
img2_representation = custom_model.predict(img2)[0,:] img2_representation = custom_model.predict(img2)[0,:]
#---------------------- #----------------------
#find distances between embeddings #find distances between embeddings
for j in metrics: for j in metrics:
if j == 'cosine': if j == 'cosine':
distance = dst.findCosineDistance(img1_representation, img2_representation) distance = dst.findCosineDistance(img1_representation, img2_representation)
elif j == 'euclidean': elif j == 'euclidean':
@ -176,53 +175,53 @@ def verify(img1_path, img2_path = '', model_name = 'VGG-Face', distance_metric =
distance = dst.findEuclideanDistance(dst.l2_normalize(img1_representation), dst.l2_normalize(img2_representation)) distance = dst.findEuclideanDistance(dst.l2_normalize(img1_representation), dst.l2_normalize(img2_representation))
else: else:
raise ValueError("Invalid distance_metric passed - ", distance_metric) raise ValueError("Invalid distance_metric passed - ", distance_metric)
distance = np.float64(distance) #causes trobule for euclideans in api calls if this is not set (issue #175) distance = np.float64(distance) #causes trobule for euclideans in api calls if this is not set (issue #175)
#---------------------- #----------------------
#decision #decision
if model_name != 'Ensemble': if model_name != 'Ensemble':
threshold = dst.findThreshold(i, j) threshold = dst.findThreshold(i, j)
if distance <= threshold: if distance <= threshold:
identified = True identified = True
else: else:
identified = False identified = False
resp_obj = { resp_obj = {
"verified": identified "verified": identified
, "distance": distance , "distance": distance
, "max_threshold_to_verify": threshold , "max_threshold_to_verify": threshold
, "model": model_name , "model": model_name
, "similarity_metric": distance_metric , "similarity_metric": distance_metric
} }
if bulkProcess == True: if bulkProcess == True:
resp_objects.append(resp_obj) resp_objects.append(resp_obj)
else: else:
return resp_obj return resp_obj
else: #Ensemble else: #Ensemble
#this returns same with OpenFace - euclidean_l2 #this returns same with OpenFace - euclidean_l2
if i == 'OpenFace' and j == 'euclidean': if i == 'OpenFace' and j == 'euclidean':
continue continue
else: else:
ensemble_features.append(distance) ensemble_features.append(distance)
#---------------------- #----------------------
if model_name == 'Ensemble': if model_name == 'Ensemble':
boosted_tree = Boosting.build_gbm() boosted_tree = Boosting.build_gbm()
prediction = boosted_tree.predict(np.expand_dims(np.array(ensemble_features), axis=0))[0] prediction = boosted_tree.predict(np.expand_dims(np.array(ensemble_features), axis=0))[0]
verified = np.argmax(prediction) == 1 verified = np.argmax(prediction) == 1
score = prediction[np.argmax(prediction)] score = prediction[np.argmax(prediction)]
resp_obj = { resp_obj = {
"verified": verified "verified": verified
, "score": score , "score": score
@ -230,12 +229,12 @@ def verify(img1_path, img2_path = '', model_name = 'VGG-Face', distance_metric =
, "model": ["VGG-Face", "Facenet", "OpenFace", "DeepFace"] , "model": ["VGG-Face", "Facenet", "OpenFace", "DeepFace"]
, "similarity_metric": ["cosine", "euclidean", "euclidean_l2"] , "similarity_metric": ["cosine", "euclidean", "euclidean_l2"]
} }
if bulkProcess == True: if bulkProcess == True:
resp_objects.append(resp_obj) resp_objects.append(resp_obj)
else: else:
return resp_obj return resp_obj
#---------------------- #----------------------
else: else:
@ -244,93 +243,91 @@ def verify(img1_path, img2_path = '', model_name = 'VGG-Face', distance_metric =
#------------------------- #-------------------------
toc = time.time() toc = time.time()
if bulkProcess == True: if bulkProcess == True:
resp_obj = {} resp_obj = {}
for i in range(0, len(resp_objects)): for i in range(0, len(resp_objects)):
resp_item = resp_objects[i] resp_item = resp_objects[i]
resp_obj["pair_%d" % (i+1)] = resp_item resp_obj["pair_%d" % (i+1)] = resp_item
return resp_obj return resp_obj
def analyze(img_path, actions = ['emotion', 'age', 'gender', 'race'] def analyze(img_path, actions = ['emotion', 'age', 'gender', 'race'] , models = {}, enforce_detection = True, detector_backend = 'mtcnn'):
, models = {}, enforce_detection = True
, detector_backend = 'mtcnn'):
""" """
This function analyzes facial attributes including age, gender, emotion and race This function analyzes facial attributes including age, gender, emotion and race
Parameters: Parameters:
img_path: exact image path, numpy array or base64 encoded image could be passed. If you are going to analyze lots of images, then set this to list. e.g. img_path = ['img1.jpg', 'img2.jpg'] img_path: exact image path, numpy array or base64 encoded image could be passed. If you are going to analyze lots of images, then set this to list. e.g. img_path = ['img1.jpg', 'img2.jpg']
actions (list): The default is ['age', 'gender', 'emotion', 'race']. You can drop some of those attributes. actions (list): The default is ['age', 'gender', 'emotion', 'race']. You can drop some of those attributes.
models: facial attribute analysis models are built in every call of analyze function. You can pass pre-built models to speed the function up. models: facial attribute analysis models are built in every call of analyze function. You can pass pre-built models to speed the function up.
models = {} models = {}
models['age'] = DeepFace.build_model('Age') models['age'] = DeepFace.build_model('Age')
models['gender'] = DeepFace.build_model('Gender') models['gender'] = DeepFace.build_model('Gender')
models['emotion'] = DeepFace.build_model('Emotion') models['emotion'] = DeepFace.build_model('Emotion')
models['race'] = DeepFace.build_model('race') models['race'] = DeepFace.build_model('race')
enforce_detection (boolean): The function throws exception if a face could not be detected. Set this to True if you don't want to get exception. This might be convenient for low resolution images. enforce_detection (boolean): The function throws exception if a face could not be detected. Set this to True if you don't want to get exception. This might be convenient for low resolution images.
detector_backend (string): set face detector backend as mtcnn, opencv, ssd or dlib. detector_backend (string): set face detector backend as mtcnn, opencv, ssd or dlib.
Returns: Returns:
The function returns a dictionary. If img_path is a list, then it will return list of dictionary. The function returns a dictionary. If img_path is a list, then it will return list of dictionary.
{ {
"region": {'x': 230, 'y': 120, 'w': 36, 'h': 45}, "region": {'x': 230, 'y': 120, 'w': 36, 'h': 45},
"age": 28.66, "age": 28.66,
"gender": "woman", "gender": "woman",
"dominant_emotion": "neutral", "dominant_emotion": "neutral",
"emotion": { "emotion": {
'sad': 37.65260875225067, 'sad': 37.65260875225067,
'angry': 0.15512987738475204, 'angry': 0.15512987738475204,
'surprise': 0.0022171278033056296, 'surprise': 0.0022171278033056296,
'fear': 1.2489334680140018, 'fear': 1.2489334680140018,
'happy': 4.609785228967667, 'happy': 4.609785228967667,
'disgust': 9.698561953541684e-07, 'disgust': 9.698561953541684e-07,
'neutral': 56.33133053779602 'neutral': 56.33133053779602
} }
"dominant_race": "white", "dominant_race": "white",
"race": { "race": {
'indian': 0.5480832420289516, 'indian': 0.5480832420289516,
'asian': 0.7830780930817127, 'asian': 0.7830780930817127,
'latino hispanic': 2.0677512511610985, 'latino hispanic': 2.0677512511610985,
'black': 0.06337375962175429, 'black': 0.06337375962175429,
'middle eastern': 3.088453598320484, 'middle eastern': 3.088453598320484,
'white': 93.44925880432129 'white': 93.44925880432129
} }
} }
""" """
img_paths, bulkProcess = functions.initialize_input(img_path) img_paths, bulkProcess = functions.initialize_input(img_path)
functions.initialize_detector(detector_backend = detector_backend) functions.initialize_detector(detector_backend = detector_backend)
#--------------------------------- #---------------------------------
built_models = list(models.keys()) built_models = list(models.keys())
#--------------------------------- #---------------------------------
#pre-trained models passed but it doesn't exist in actions #pre-trained models passed but it doesn't exist in actions
if len(built_models) > 0: if len(built_models) > 0:
if 'emotion' in built_models and 'emotion' not in actions: if 'emotion' in built_models and 'emotion' not in actions:
actions.append('emotion') actions.append('emotion')
if 'age' in built_models and 'age' not in actions: if 'age' in built_models and 'age' not in actions:
actions.append('age') actions.append('age')
if 'gender' in built_models and 'gender' not in actions: if 'gender' in built_models and 'gender' not in actions:
actions.append('gender') actions.append('gender')
if 'race' in built_models and 'race' not in actions: if 'race' in built_models and 'race' not in actions:
actions.append('race') actions.append('race')
#--------------------------------- #---------------------------------
if 'emotion' in actions and 'emotion' not in built_models: if 'emotion' in actions and 'emotion' not in built_models:
@ -344,20 +341,20 @@ def analyze(img_path, actions = ['emotion', 'age', 'gender', 'race']
if 'race' in actions and 'race' not in built_models: if 'race' in actions and 'race' not in built_models:
models['race'] = build_model('Race') models['race'] = build_model('Race')
#--------------------------------- #---------------------------------
resp_objects = [] resp_objects = []
disable_option = False if len(img_paths) > 1 else True disable_option = False if len(img_paths) > 1 else True
global_pbar = tqdm(range(0,len(img_paths)), desc='Analyzing', disable = disable_option) global_pbar = tqdm(range(0,len(img_paths)), desc='Analyzing', disable = disable_option)
for j in global_pbar: for j in global_pbar:
img_path = img_paths[j] img_path = img_paths[j]
resp_obj = {} resp_obj = {}
disable_option = False if len(actions) > 1 else True disable_option = False if len(actions) > 1 else True
pbar = tqdm(range(0,len(actions)), desc='Finding actions', disable = disable_option) pbar = tqdm(range(0,len(actions)), desc='Finding actions', disable = disable_option)
@ -366,12 +363,12 @@ def analyze(img_path, actions = ['emotion', 'age', 'gender', 'race']
region = [] # x, y, w, h of the detected face region region = [] # x, y, w, h of the detected face region
region_labels = ['x', 'y', 'w', 'h'] region_labels = ['x', 'y', 'w', 'h']
#facial attribute analysis #facial attribute analysis
for index in pbar: for index in pbar:
action = actions[index] action = actions[index]
pbar.set_description("Action: %s" % (action)) pbar.set_description("Action: %s" % (action))
if action == 'emotion': if action == 'emotion':
emotion_labels = ['angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral'] emotion_labels = ['angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral']
img, region = functions.preprocess_face(img = img_path, target_size = (48, 48), grayscale = True, enforce_detection = enforce_detection, detector_backend = detector_backend, return_region = True) img, region = functions.preprocess_face(img = img_path, target_size = (48, 48), grayscale = True, enforce_detection = enforce_detection, detector_backend = detector_backend, return_region = True)
@ -386,23 +383,23 @@ def analyze(img_path, actions = ['emotion', 'age', 'gender', 'race']
sum_of_predictions = emotion_predictions.sum() sum_of_predictions = emotion_predictions.sum()
resp_obj["emotion"] = {} resp_obj["emotion"] = {}
for i in range(0, len(emotion_labels)): for i in range(0, len(emotion_labels)):
emotion_label = emotion_labels[i] emotion_label = emotion_labels[i]
emotion_prediction = 100 * emotion_predictions[i] / sum_of_predictions emotion_prediction = 100 * emotion_predictions[i] / sum_of_predictions
resp_obj["emotion"][emotion_label] = emotion_prediction resp_obj["emotion"][emotion_label] = emotion_prediction
resp_obj["dominant_emotion"] = emotion_labels[np.argmax(emotion_predictions)] resp_obj["dominant_emotion"] = emotion_labels[np.argmax(emotion_predictions)]
elif action == 'age': elif action == 'age':
if img_224 is None: if img_224 is None:
img_224, region = functions.preprocess_face(img = img_path, target_size = (224, 224), grayscale = False, enforce_detection = enforce_detection, detector_backend = detector_backend, return_region = True) img_224, region = functions.preprocess_face(img = img_path, target_size = (224, 224), grayscale = False, enforce_detection = enforce_detection, detector_backend = detector_backend, return_region = True)
resp_obj["region"] = {} resp_obj["region"] = {}
for i, parameter in enumerate(region_labels): for i, parameter in enumerate(region_labels):
resp_obj["region"][parameter] = region[i] resp_obj["region"][parameter] = region[i]
age_predictions = models['age'].predict(img_224)[0,:] age_predictions = models['age'].predict(img_224)[0,:]
apparent_age = Age.findApparentAge(age_predictions) apparent_age = Age.findApparentAge(age_predictions)
@ -416,7 +413,7 @@ def analyze(img_path, actions = ['emotion', 'age', 'gender', 'race']
for i, parameter in enumerate(region_labels): for i, parameter in enumerate(region_labels):
resp_obj["region"][parameter] = region[i] resp_obj["region"][parameter] = region[i]
gender_prediction = models['gender'].predict(img_224)[0,:] gender_prediction = models['gender'].predict(img_224)[0,:]
if np.argmax(gender_prediction) == 0: if np.argmax(gender_prediction) == 0:
@ -436,28 +433,28 @@ def analyze(img_path, actions = ['emotion', 'age', 'gender', 'race']
for i, parameter in enumerate(region_labels): for i, parameter in enumerate(region_labels):
resp_obj["region"][parameter] = region[i] resp_obj["region"][parameter] = region[i]
sum_of_predictions = race_predictions.sum() sum_of_predictions = race_predictions.sum()
resp_obj["race"] = {} resp_obj["race"] = {}
for i in range(0, len(race_labels)): for i in range(0, len(race_labels)):
race_label = race_labels[i] race_label = race_labels[i]
race_prediction = 100 * race_predictions[i] / sum_of_predictions race_prediction = 100 * race_predictions[i] / sum_of_predictions
resp_obj["race"][race_label] = race_prediction resp_obj["race"][race_label] = race_prediction
resp_obj["dominant_race"] = race_labels[np.argmax(race_predictions)] resp_obj["dominant_race"] = race_labels[np.argmax(race_predictions)]
#--------------------------------- #---------------------------------
if bulkProcess == True: if bulkProcess == True:
resp_objects.append(resp_obj) resp_objects.append(resp_obj)
else: else:
return resp_obj return resp_obj
if bulkProcess == True: if bulkProcess == True:
resp_obj = {} resp_obj = {}
for i in range(0, len(resp_objects)): for i in range(0, len(resp_objects)):
resp_item = resp_objects[i] resp_item = resp_objects[i]
resp_obj["instance_%d" % (i+1)] = resp_item resp_obj["instance_%d" % (i+1)] = resp_item
@ -465,63 +462,63 @@ def analyze(img_path, actions = ['emotion', 'age', 'gender', 'race']
return resp_obj return resp_obj
def find(img_path, db_path, model_name ='VGG-Face', distance_metric = 'cosine', model = None, enforce_detection = True, detector_backend = 'mtcnn'): def find(img_path, db_path, model_name ='VGG-Face', distance_metric = 'cosine', model = None, enforce_detection = True, detector_backend = 'mtcnn'):
""" """
This function applies verification several times and find an identity in a database This function applies verification several times and find an identity in a database
Parameters: Parameters:
img_path: exact image path, numpy array or based64 encoded image. If you are going to find several identities, then you should pass img_path as array instead of calling find function in a for loop. e.g. img_path = ["img1.jpg", "img2.jpg"] img_path: exact image path, numpy array or based64 encoded image. If you are going to find several identities, then you should pass img_path as array instead of calling find function in a for loop. e.g. img_path = ["img1.jpg", "img2.jpg"]
db_path (string): You should store some .jpg files in a folder and pass the exact folder path to this. db_path (string): You should store some .jpg files in a folder and pass the exact folder path to this.
model_name (string): VGG-Face, Facenet, OpenFace, DeepFace, DeepID, Dlib or Ensemble model_name (string): VGG-Face, Facenet, OpenFace, DeepFace, DeepID, Dlib or Ensemble
distance_metric (string): cosine, euclidean, euclidean_l2 distance_metric (string): cosine, euclidean, euclidean_l2
model: built deepface model. A face recognition models are built in every call of find function. You can pass pre-built models to speed the function up. model: built deepface model. A face recognition models are built in every call of find function. You can pass pre-built models to speed the function up.
model = DeepFace.build_model('VGG-Face') model = DeepFace.build_model('VGG-Face')
enforce_detection (boolean): The function throws exception if a face could not be detected. Set this to True if you don't want to get exception. This might be convenient for low resolution images. enforce_detection (boolean): The function throws exception if a face could not be detected. Set this to True if you don't want to get exception. This might be convenient for low resolution images.
detector_backend (string): set face detector backend as mtcnn, opencv, ssd or dlib detector_backend (string): set face detector backend as mtcnn, opencv, ssd or dlib
Returns: Returns:
This function returns pandas data frame. If a list of images is passed to img_path, then it will return list of pandas data frame. This function returns pandas data frame. If a list of images is passed to img_path, then it will return list of pandas data frame.
""" """
tic = time.time() tic = time.time()
img_paths, bulkProcess = functions.initialize_input(img_path) img_paths, bulkProcess = functions.initialize_input(img_path)
functions.initialize_detector(detector_backend = detector_backend) functions.initialize_detector(detector_backend = detector_backend)
#------------------------------- #-------------------------------
if os.path.isdir(db_path) == True: if os.path.isdir(db_path) == True:
if model == None: if model == None:
if model_name == 'Ensemble': if model_name == 'Ensemble':
print("Ensemble learning enabled") print("Ensemble learning enabled")
models = Boosting.loadModel() models = Boosting.loadModel()
else: #model is not ensemble else: #model is not ensemble
model = build_model(model_name) model = build_model(model_name)
models = {} models = {}
models[model_name] = model models[model_name] = model
else: #model != None else: #model != None
print("Already built model is passed") print("Already built model is passed")
if model_name == 'Ensemble': if model_name == 'Ensemble':
Boosting.validate_model(model) Boosting.validate_model(model)
models = model.copy() models = model.copy()
else: else:
models = {} models = {}
models[model_name] = model models[model_name] = model
#--------------------------------------- #---------------------------------------
if model_name == 'Ensemble': if model_name == 'Ensemble':
model_names = ['VGG-Face', 'Facenet', 'OpenFace', 'DeepFace'] model_names = ['VGG-Face', 'Facenet', 'OpenFace', 'DeepFace']
metric_names = ['cosine', 'euclidean', 'euclidean_l2'] metric_names = ['cosine', 'euclidean', 'euclidean_l2']
@ -529,149 +526,149 @@ def find(img_path, db_path, model_name ='VGG-Face', distance_metric = 'cosine',
model_names = []; metric_names = [] model_names = []; metric_names = []
model_names.append(model_name) model_names.append(model_name)
metric_names.append(distance_metric) metric_names.append(distance_metric)
#--------------------------------------- #---------------------------------------
file_name = "representations_%s.pkl" % (model_name) file_name = "representations_%s.pkl" % (model_name)
file_name = file_name.replace("-", "_").lower() file_name = file_name.replace("-", "_").lower()
if path.exists(db_path+"/"+file_name): if path.exists(db_path+"/"+file_name):
print("WARNING: Representations for images in ",db_path," folder were previously stored in ", file_name, ". If you added new instances after this file creation, then please delete this file and call find function again. It will create it again.") print("WARNING: Representations for images in ",db_path," folder were previously stored in ", file_name, ". If you added new instances after this file creation, then please delete this file and call find function again. It will create it again.")
f = open(db_path+'/'+file_name, 'rb') f = open(db_path+'/'+file_name, 'rb')
representations = pickle.load(f) representations = pickle.load(f)
print("There are ", len(representations)," representations found in ",file_name) print("There are ", len(representations)," representations found in ",file_name)
else: #create representation.pkl from scratch else: #create representation.pkl from scratch
employees = [] employees = []
for r, d, f in os.walk(db_path): # r=root, d=directories, f = files for r, d, f in os.walk(db_path): # r=root, d=directories, f = files
for file in f: for file in f:
if ('.jpg' in file.lower()) or ('.png' in file.lower()): if ('.jpg' in file.lower()) or ('.png' in file.lower()):
exact_path = r + "/" + file exact_path = r + "/" + file
employees.append(exact_path) employees.append(exact_path)
if len(employees) == 0: if len(employees) == 0:
raise ValueError("There is no image in ", db_path," folder! Validate .jpg or .png files exist in this path.") raise ValueError("There is no image in ", db_path," folder! Validate .jpg or .png files exist in this path.")
#------------------------ #------------------------
#find representations for db images #find representations for db images
representations = [] representations = []
pbar = tqdm(range(0,len(employees)), desc='Finding representations') pbar = tqdm(range(0,len(employees)), desc='Finding representations')
#for employee in employees: #for employee in employees:
for index in pbar: for index in pbar:
employee = employees[index] employee = employees[index]
instance = [] instance = []
instance.append(employee) instance.append(employee)
for j in model_names: for j in model_names:
custom_model = models[j] custom_model = models[j]
#---------------------------------- #----------------------------------
#decide input shape #decide input shape
input_shape = functions.find_input_shape(custom_model) input_shape = functions.find_input_shape(custom_model)
input_shape_x = input_shape[0]; input_shape_y = input_shape[1] input_shape_x = input_shape[0]; input_shape_y = input_shape[1]
#---------------------------------- #----------------------------------
img = functions.preprocess_face(img = employee img = functions.preprocess_face(img = employee
, target_size = (input_shape_y, input_shape_x) , target_size = (input_shape_y, input_shape_x)
, enforce_detection = enforce_detection , enforce_detection = enforce_detection
, detector_backend = detector_backend) , detector_backend = detector_backend)
representation = custom_model.predict(img)[0,:] representation = custom_model.predict(img)[0,:]
instance.append(representation) instance.append(representation)
#------------------------------- #-------------------------------
representations.append(instance) representations.append(instance)
f = open(db_path+'/'+file_name, "wb") f = open(db_path+'/'+file_name, "wb")
pickle.dump(representations, f) pickle.dump(representations, f)
f.close() f.close()
print("Representations stored in ",db_path,"/",file_name," file. Please delete this file when you add new identities in your database.") print("Representations stored in ",db_path,"/",file_name," file. Please delete this file when you add new identities in your database.")
#---------------------------- #----------------------------
#now, we got representations for facial database #now, we got representations for facial database
if model_name != 'Ensemble': if model_name != 'Ensemble':
df = pd.DataFrame(representations, columns = ["identity", "%s_representation" % (model_name)]) df = pd.DataFrame(representations, columns = ["identity", "%s_representation" % (model_name)])
else: #ensemble learning else: #ensemble learning
columns = ['identity'] columns = ['identity']
[columns.append('%s_representation' % i) for i in model_names] [columns.append('%s_representation' % i) for i in model_names]
df = pd.DataFrame(representations, columns = columns) df = pd.DataFrame(representations, columns = columns)
df_base = df.copy() #df will be filtered in each img. we will restore it for the next item. df_base = df.copy() #df will be filtered in each img. we will restore it for the next item.
resp_obj = [] resp_obj = []
global_pbar = tqdm(range(0,len(img_paths)), desc='Analyzing') global_pbar = tqdm(range(0,len(img_paths)), desc='Analyzing')
for j in global_pbar: for j in global_pbar:
img_path = img_paths[j] img_path = img_paths[j]
#find representation for passed image #find representation for passed image
for j in model_names: for j in model_names:
custom_model = models[j] custom_model = models[j]
#-------------------------------- #--------------------------------
#decide input shape #decide input shape
input_shape = functions.find_input_shape(custom_model) input_shape = functions.find_input_shape(custom_model)
input_shape_x = input_shape[0]; input_shape_y = input_shape[1] input_shape_x = input_shape[0]; input_shape_y = input_shape[1]
#-------------------------------- #--------------------------------
img = functions.preprocess_face(img = img_path, target_size = (input_shape_y, input_shape_x) img = functions.preprocess_face(img = img_path, target_size = (input_shape_y, input_shape_x)
, enforce_detection = enforce_detection , enforce_detection = enforce_detection
, detector_backend = detector_backend) , detector_backend = detector_backend)
target_representation = custom_model.predict(img)[0,:] target_representation = custom_model.predict(img)[0,:]
for k in metric_names: for k in metric_names:
distances = [] distances = []
for index, instance in df.iterrows(): for index, instance in df.iterrows():
source_representation = instance["%s_representation" % (j)] source_representation = instance["%s_representation" % (j)]
if k == 'cosine': if k == 'cosine':
distance = dst.findCosineDistance(source_representation, target_representation) distance = dst.findCosineDistance(source_representation, target_representation)
elif k == 'euclidean': elif k == 'euclidean':
distance = dst.findEuclideanDistance(source_representation, target_representation) distance = dst.findEuclideanDistance(source_representation, target_representation)
elif k == 'euclidean_l2': elif k == 'euclidean_l2':
distance = dst.findEuclideanDistance(dst.l2_normalize(source_representation), dst.l2_normalize(target_representation)) distance = dst.findEuclideanDistance(dst.l2_normalize(source_representation), dst.l2_normalize(target_representation))
distances.append(distance) distances.append(distance)
#--------------------------- #---------------------------
if model_name == 'Ensemble' and j == 'OpenFace' and k == 'euclidean': if model_name == 'Ensemble' and j == 'OpenFace' and k == 'euclidean':
continue continue
else: else:
df["%s_%s" % (j, k)] = distances df["%s_%s" % (j, k)] = distances
if model_name != 'Ensemble': if model_name != 'Ensemble':
threshold = dst.findThreshold(j, k) threshold = dst.findThreshold(j, k)
df = df.drop(columns = ["%s_representation" % (j)]) df = df.drop(columns = ["%s_representation" % (j)])
df = df[df["%s_%s" % (j, k)] <= threshold] df = df[df["%s_%s" % (j, k)] <= threshold]
df = df.sort_values(by = ["%s_%s" % (j, k)], ascending=True).reset_index(drop=True) df = df.sort_values(by = ["%s_%s" % (j, k)], ascending=True).reset_index(drop=True)
resp_obj.append(df) resp_obj.append(df)
df = df_base.copy() #restore df for the next iteration df = df_base.copy() #restore df for the next iteration
#---------------------------------- #----------------------------------
if model_name == 'Ensemble': if model_name == 'Ensemble':
feature_names = [] feature_names = []
for j in model_names: for j in model_names:
for k in metric_names: for k in metric_names:
@ -680,106 +677,145 @@ def find(img_path, db_path, model_name ='VGG-Face', distance_metric = 'cosine',
else: else:
feature = '%s_%s' % (j, k) feature = '%s_%s' % (j, k)
feature_names.append(feature) feature_names.append(feature)
#print(df.head()) #print(df.head())
x = df[feature_names].values x = df[feature_names].values
#-------------------------------------- #--------------------------------------
boosted_tree = Boosting.build_gbm() boosted_tree = Boosting.build_gbm()
y = boosted_tree.predict(x) y = boosted_tree.predict(x)
verified_labels = []; scores = [] verified_labels = []; scores = []
for i in y: for i in y:
verified = np.argmax(i) == 1 verified = np.argmax(i) == 1
score = i[np.argmax(i)] score = i[np.argmax(i)]
verified_labels.append(verified) verified_labels.append(verified)
scores.append(score) scores.append(score)
df['verified'] = verified_labels df['verified'] = verified_labels
df['score'] = scores df['score'] = scores
df = df[df.verified == True] df = df[df.verified == True]
#df = df[df.score > 0.99] #confidence score #df = df[df.score > 0.99] #confidence score
df = df.sort_values(by = ["score"], ascending=False).reset_index(drop=True) df = df.sort_values(by = ["score"], ascending=False).reset_index(drop=True)
df = df[['identity', 'verified', 'score']] df = df[['identity', 'verified', 'score']]
resp_obj.append(df) resp_obj.append(df)
df = df_base.copy() #restore df for the next iteration df = df_base.copy() #restore df for the next iteration
#---------------------------------- #----------------------------------
toc = time.time() toc = time.time()
print("find function lasts ",toc-tic," seconds") print("find function lasts ",toc-tic," seconds")
if len(resp_obj) == 1: if len(resp_obj) == 1:
return resp_obj[0] return resp_obj[0]
return resp_obj return resp_obj
else: else:
raise ValueError("Passed db_path does not exist!") raise ValueError("Passed db_path does not exist!")
return None return None
def stream(db_path = '', model_name ='VGG-Face', distance_metric = 'cosine' def represent(img_path, model_name = 'VGG-Face', distance_metric = 'euclidean', model = None, enforce_detection = True, detector_backend = 'mtcnn'):
, enable_face_analysis = True
, source = 0, time_threshold = 5, frame_threshold = 5): """
This function represents facial images as vectors.
Parameters:
img_path: exact image path, numpy array or based64 encoded images could be passed.
model_name (string): VGG-Face, Facenet, OpenFace, DeepFace, DeepID, Dlib, ArcFace.
distance_metric (string): cosine, euclidean, euclidean_l2
model: Built deepface model. A face recognition model is built every call of verify function. You can pass pre-built face recognition model optionally if you will call verify function several times. Consider to pass model if you are going to call represent function in a for loop.
model = DeepFace.build_model('VGG-Face')
enforce_detection (boolean): If any face could not be detected in an image, then verify function will return exception. Set this to False not to have this exception. This might be convenient for low resolution images.
detector_backend (string): set face detector backend as mtcnn, opencv, ssd or dlib
Returns:
Represent function returns a multidimensional vector. The number of dimensions is changing based on the reference model. E.g. FaceNet returns 128 dimensional vector; VGG-Face returns 2622 dimensional vector.
"""
if model is None:
model = build_model(model_name)
#---------------------------------
#decide input shape
input_shape = input_shape_x, input_shape_y= functions.find_input_shape(model)
img = functions.preprocess_face(img = img_path
, target_size=(input_shape_y, input_shape_x)
, enforce_detection = enforce_detection
, detector_backend = detector_backend)
embedding = model.predict(img)[0].tolist()
return embedding
def stream(db_path = '', model_name ='VGG-Face', distance_metric = 'cosine', enable_face_analysis = True, source = 0, time_threshold = 5, frame_threshold = 5):
""" """
This function applies real time face recognition and facial attribute analysis This function applies real time face recognition and facial attribute analysis
Parameters: Parameters:
db_path (string): facial database path. You should store some .jpg files in this folder. db_path (string): facial database path. You should store some .jpg files in this folder.
model_name (string): VGG-Face, Facenet, OpenFace, DeepFace, DeepID, Dlib or Ensemble model_name (string): VGG-Face, Facenet, OpenFace, DeepFace, DeepID, Dlib or Ensemble
distance_metric (string): cosine, euclidean, euclidean_l2 distance_metric (string): cosine, euclidean, euclidean_l2
enable_facial_analysis (boolean): Set this to False to just run face recognition enable_facial_analysis (boolean): Set this to False to just run face recognition
source: Set this to 0 for access web cam. Otherwise, pass exact video path. source: Set this to 0 for access web cam. Otherwise, pass exact video path.
time_threshold (int): how many second analyzed image will be displayed time_threshold (int): how many second analyzed image will be displayed
frame_threshold (int): how many frames required to focus on face frame_threshold (int): how many frames required to focus on face
""" """
if time_threshold < 1: if time_threshold < 1:
raise ValueError("time_threshold must be greater than the value 1 but you passed "+str(time_threshold)) raise ValueError("time_threshold must be greater than the value 1 but you passed "+str(time_threshold))
if frame_threshold < 1: if frame_threshold < 1:
raise ValueError("frame_threshold must be greater than the value 1 but you passed "+str(frame_threshold)) raise ValueError("frame_threshold must be greater than the value 1 but you passed "+str(frame_threshold))
functions.initialize_detector(detector_backend = 'opencv') functions.initialize_detector(detector_backend = 'opencv')
realtime.analysis(db_path, model_name, distance_metric, enable_face_analysis realtime.analysis(db_path, model_name, distance_metric, enable_face_analysis
, source = source, time_threshold = time_threshold, frame_threshold = frame_threshold) , source = source, time_threshold = time_threshold, frame_threshold = frame_threshold)
def detectFace(img_path, detector_backend = 'mtcnn'): def detectFace(img_path, detector_backend = 'mtcnn'):
""" """
This function applies pre-processing stages of a face recognition pipeline including detection and alignment This function applies pre-processing stages of a face recognition pipeline including detection and alignment
Parameters: Parameters:
img_path: exact image path, numpy array or base64 encoded image img_path: exact image path, numpy array or base64 encoded image
detector_backend (string): face detection backends are mtcnn, opencv, ssd or dlib detector_backend (string): face detection backends are mtcnn, opencv, ssd or dlib
Returns: Returns:
deteced and aligned face in numpy format deteced and aligned face in numpy format
""" """
functions.initialize_detector(detector_backend = detector_backend) functions.initialize_detector(detector_backend = detector_backend)
img = functions.preprocess_face(img = img_path, detector_backend = detector_backend)[0] #preprocess_face returns (1, 224, 224, 3) img = functions.preprocess_face(img = img_path, detector_backend = detector_backend)[0] #preprocess_face returns (1, 224, 224, 3)
return img[:, :, ::-1] #bgr to rgb return img[:, :, ::-1] #bgr to rgb
#--------------------------- #---------------------------
#main #main

View File

@ -26,6 +26,18 @@ from deepface.extendedmodels import Age, Gender, Race, Emotion
#----------------------------------------- #-----------------------------------------
img_path = "dataset/img1.jpg"
embedding = DeepFace.represent(img_path)
print("Function returned ", len(embedding), "dimensional vector")
model_name = "VGG-Face"
model = DeepFace.build_model(model_name)
print(model_name," is built")
embedding = DeepFace.represent(img_path, model = model)
print("Represent function returned ", len(embedding), "dimensional vector")
#-----------------------------------------
dataset = [ dataset = [
['dataset/img1.jpg', 'dataset/img2.jpg', True], ['dataset/img1.jpg', 'dataset/img2.jpg', True],
['dataset/img1.jpg', 'dataset/img6.jpg', True] ['dataset/img1.jpg', 'dataset/img6.jpg', True]
@ -64,9 +76,9 @@ print("-----------------------------------------")
print("Pre-built model for single find function test") print("Pre-built model for single find function test")
model_name = "VGG-Face" #model_name = "VGG-Face"
model = DeepFace.build_model(model_name) #model = DeepFace.build_model(model_name)
print(model_name," is built") #print(model_name," is built")
df = DeepFace.find(img_path = "dataset/img1.jpg", db_path = "dataset" df = DeepFace.find(img_path = "dataset/img1.jpg", db_path = "dataset"
, model_name = model_name, model = model , model_name = model_name, model = model
@ -140,10 +152,10 @@ dataset = [
['dataset/img5.jpg', 'dataset/img6.jpg', True], ['dataset/img5.jpg', 'dataset/img6.jpg', True],
['dataset/img6.jpg', 'dataset/img7.jpg', True], ['dataset/img6.jpg', 'dataset/img7.jpg', True],
['dataset/img8.jpg', 'dataset/img9.jpg', True], ['dataset/img8.jpg', 'dataset/img9.jpg', True],
['dataset/img1.jpg', 'dataset/img11.jpg', True], ['dataset/img1.jpg', 'dataset/img11.jpg', True],
['dataset/img2.jpg', 'dataset/img11.jpg', True], ['dataset/img2.jpg', 'dataset/img11.jpg', True],
['dataset/img1.jpg', 'dataset/img3.jpg', False], ['dataset/img1.jpg', 'dataset/img3.jpg', False],
['dataset/img2.jpg', 'dataset/img3.jpg', False], ['dataset/img2.jpg', 'dataset/img3.jpg', False],
['dataset/img6.jpg', 'dataset/img8.jpg', False], ['dataset/img6.jpg', 'dataset/img8.jpg', False],
@ -163,29 +175,29 @@ for model in models:
img1 = instance[0] img1 = instance[0]
img2 = instance[1] img2 = instance[1]
result = instance[2] result = instance[2]
resp_obj = DeepFace.verify(img1, img2 resp_obj = DeepFace.verify(img1, img2
, model_name = model, model = prebuilt_model , model_name = model, model = prebuilt_model
, distance_metric = metric) , distance_metric = metric)
prediction = resp_obj["verified"] prediction = resp_obj["verified"]
distance = round(resp_obj["distance"], 2) distance = round(resp_obj["distance"], 2)
required_threshold = resp_obj["max_threshold_to_verify"] required_threshold = resp_obj["max_threshold_to_verify"]
test_result_label = "failed" test_result_label = "failed"
if prediction == result: if prediction == result:
passed_tests = passed_tests + 1 passed_tests = passed_tests + 1
test_result_label = "passed" test_result_label = "passed"
if prediction == True: if prediction == True:
classified_label = "verified" classified_label = "verified"
else: else:
classified_label = "unverified" classified_label = "unverified"
test_cases = test_cases + 1 test_cases = test_cases + 1
print(img1.split("/")[-1], "-", img2.split("/")[-1], classified_label, "as same person based on", model,"and",metric,". Distance:",distance,", Threshold:", required_threshold,"(",test_result_label,")") print(img1.split("/")[-1], "-", img2.split("/")[-1], classified_label, "as same person based on", model,"and",metric,". Distance:",distance,", Threshold:", required_threshold,"(",test_result_label,")")
print("--------------------------") print("--------------------------")
#----------------------------------------- #-----------------------------------------