From 56adc351f22047c5c70fcf92cd97130ba27a707c Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Mon, 10 Oct 2022 09:00:53 +0300 Subject: [PATCH 1/6] citation purposes --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a0b39bd..369152e 100644 --- a/README.md +++ b/README.md @@ -301,7 +301,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 for facial recognition purposes. Here are its BibTex entries: +Please cite deepface in your publications if it helps your research. Here are its BibTex entries: + +If you use deepface for facial recogntion purposes, please cite the this publication. ```BibTeX @inproceedings{serengil2020lightface, @@ -316,7 +318,7 @@ Please cite deepface in your publications if it helps your research for facial r } ``` -If you use deepface for facial attribute analysis purposes such as age, gender, emotion or ethnicity, please cite the this publication. +If you use deepface for facial attribute analysis purposes such as age, gender, emotion or ethnicity prediction, please cite the this publication. ```BibTeX @inproceedings{serengil2021lightface, From a807284e2a470d004681f2a40e00056194a70845 Mon Sep 17 00:00:00 2001 From: Hoan Nguyen Date: Fri, 21 Oct 2022 10:01:12 +0200 Subject: [PATCH 2/6] feat(verbose): Silent tensorflow models output to stdout --- deepface/DeepFace.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index 7d8040c..3916566 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -367,6 +367,7 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = resp_objects = [] disable_option = (False if len(img_paths) > 1 else True) or not prog_bar + verbose = int(not disable_option) global_pbar = tqdm(range(0,len(img_paths)), desc='Analyzing', disable = disable_option) @@ -395,7 +396,7 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = 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) - emotion_predictions = models['emotion'].predict(img)[0,:] + emotion_predictions = models['emotion'].predict(img, verbose=verbose)[0,:] sum_of_predictions = emotion_predictions.sum() @@ -412,7 +413,7 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = 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) - age_predictions = models['age'].predict(img_224)[0,:] + age_predictions = models['age'].predict(img_224, verbose=verbose)[0,:] apparent_age = Age.findApparentAge(age_predictions) resp_obj["age"] = int(apparent_age) #int cast is for the exception - object of type 'float32' is not JSON serializable @@ -422,7 +423,7 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = 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) - gender_predictions = models['gender'].predict(img_224)[0,:] + gender_predictions = models['gender'].predict(img_224, verbose=verbose)[0,:] gender_labels = ["Woman", "Man"] resp_obj["gender"] = {} @@ -436,7 +437,7 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = elif action == 'race': 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) #just emotion model expects grayscale images - race_predictions = models['race'].predict(img_224)[0,:] + race_predictions = models['race'].predict(img_224, verbose=verbose)[0,:] race_labels = ['asian', 'indian', 'black', 'white', 'middle eastern', 'latino hispanic'] sum_of_predictions = race_predictions.sum() From d96b4bdbc5393107a4f97e864579bb1e8d37cf15 Mon Sep 17 00:00:00 2001 From: Andrea Oliveri Date: Sun, 23 Oct 2022 15:46:55 +0200 Subject: [PATCH 3/6] Added confidence to FaceDetector.detect_face and .detect_faces methods --- deepface/commons/functions.py | 2 +- deepface/commons/realtime.py | 2 +- deepface/detectors/DlibWrapper.py | 8 ++++++-- deepface/detectors/FaceDetector.py | 11 ++++++----- deepface/detectors/MediapipeWrapper.py | 6 +++--- deepface/detectors/MtcnnWrapper.py | 4 +++- deepface/detectors/OpenCvWrapper.py | 9 ++++++--- deepface/detectors/RetinaFaceWrapper.py | 3 ++- deepface/detectors/SsdWrapper.py | 4 +++- 9 files changed, 31 insertions(+), 18 deletions(-) diff --git a/deepface/commons/functions.py b/deepface/commons/functions.py index ec034ad..76ccdee 100644 --- a/deepface/commons/functions.py +++ b/deepface/commons/functions.py @@ -110,7 +110,7 @@ def detect_face(img, detector_backend = 'opencv', grayscale = False, enforce_det face_detector = FaceDetector.build_model(detector_backend) try: - detected_face, img_region = FaceDetector.detect_face(face_detector, detector_backend, img, align) + detected_face, img_region, _ = FaceDetector.detect_face(face_detector, detector_backend, img, align) except: #if detected face shape is (0, 0) and alignment cannot be performed, this block will be run detected_face = None diff --git a/deepface/commons/realtime.py b/deepface/commons/realtime.py index f071ab5..4a65621 100644 --- a/deepface/commons/realtime.py +++ b/deepface/commons/realtime.py @@ -151,7 +151,7 @@ def analysis(db_path, model_name = 'VGG-Face', detector_backend = 'opencv', dist detected_faces = [] face_index = 0 - for face, (x, y, w, h) in faces: + for face, (x, y, w, h), _ in faces: if w > 130: #discard small detected faces face_detected = True diff --git a/deepface/detectors/DlibWrapper.py b/deepface/detectors/DlibWrapper.py index d805ee8..2536892 100644 --- a/deepface/detectors/DlibWrapper.py +++ b/deepface/detectors/DlibWrapper.py @@ -46,9 +46,12 @@ def detect_face(detector, img, align = True): detected_face = None img_region = [0, 0, img.shape[0], img.shape[1]] + confidence = None face_detector = detector["face_detector"] - detections = face_detector(img, 1) + + #note that, by design, dlib's fhog face detector scores are >0 but not capped at 1 + detections, scores, _ = face_detector.run(img, 1) if len(detections) > 0: @@ -60,12 +63,13 @@ def detect_face(detector, img, align = True): detected_face = img[max(0, top): min(bottom, img.shape[0]), max(0, left): min(right, img.shape[1])] img_region = [left, top, right - left, bottom - top] + confidence = scores[idx] if align: img_shape = sp(img, detections[idx]) detected_face = dlib.get_face_chip(img, img_shape, size = detected_face.shape[0]) - resp.append((detected_face, img_region)) + resp.append((detected_face, img_region, confidence)) return resp diff --git a/deepface/detectors/FaceDetector.py b/deepface/detectors/FaceDetector.py index 971670f..77d734a 100644 --- a/deepface/detectors/FaceDetector.py +++ b/deepface/detectors/FaceDetector.py @@ -14,7 +14,7 @@ def build_model(detector_backend): 'dlib': DlibWrapper.build_model, 'mtcnn': MtcnnWrapper.build_model, 'retinaface': RetinaFaceWrapper.build_model, - 'mediapipe': MediapipeWrapper.build_model + 'mediapipe': MediapipeWrapper.build_model } if not "face_detector_obj" in globals(): @@ -37,12 +37,13 @@ def detect_face(face_detector, detector_backend, img, align = True): obj = detect_faces(face_detector, detector_backend, img, align) if len(obj) > 0: - face, region = obj[0] #discard multiple faces + face, region, confidence = obj[0] #discard multiple faces else: #len(obj) == 0 face = None region = [0, 0, img.shape[0], img.shape[1]] + confidence = None - return face, region + return face, region, confidence def detect_faces(face_detector, detector_backend, img, align = True): @@ -52,14 +53,14 @@ def detect_faces(face_detector, detector_backend, img, align = True): 'dlib': DlibWrapper.detect_face, 'mtcnn': MtcnnWrapper.detect_face, 'retinaface': RetinaFaceWrapper.detect_face, - 'mediapipe': MediapipeWrapper.detect_face + 'mediapipe': MediapipeWrapper.detect_face } detect_face = backends.get(detector_backend) if detect_face: obj = detect_face(face_detector, img, align) - #obj stores list of detected_face and region pair + #obj stores list of (detected_face, region, confidence) return obj else: diff --git a/deepface/detectors/MediapipeWrapper.py b/deepface/detectors/MediapipeWrapper.py index 5cce678..239ea80 100644 --- a/deepface/detectors/MediapipeWrapper.py +++ b/deepface/detectors/MediapipeWrapper.py @@ -19,8 +19,8 @@ def detect_face(face_detector, img, align = True): if results.detections: for detection in results.detections: - - confidence = detection.score + + confidence, = detection.score bounding_box = detection.location_data.relative_bounding_box landmarks = detection.location_data.relative_keypoints @@ -44,6 +44,6 @@ def detect_face(face_detector, img, align = True): if align: detected_face = FaceDetector.alignment_procedure(detected_face, left_eye, right_eye) - resp.append((detected_face,img_region)) + resp.append((detected_face, img_region, confidence)) return resp diff --git a/deepface/detectors/MtcnnWrapper.py b/deepface/detectors/MtcnnWrapper.py index aa56533..9e9607f 100644 --- a/deepface/detectors/MtcnnWrapper.py +++ b/deepface/detectors/MtcnnWrapper.py @@ -12,6 +12,7 @@ def detect_face(face_detector, img, align = True): detected_face = None img_region = [0, 0, img.shape[0], img.shape[1]] + confidence = None img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #mtcnn expects RGB but OpenCV read BGR detections = face_detector.detect_faces(img_rgb) @@ -22,6 +23,7 @@ def detect_face(face_detector, img, align = True): x, y, w, h = detection["box"] detected_face = img[int(y):int(y+h), int(x):int(x+w)] img_region = [x, y, w, h] + confidence = detection["confidence"] if align: keypoints = detection["keypoints"] @@ -29,6 +31,6 @@ def detect_face(face_detector, img, align = True): right_eye = keypoints["right_eye"] detected_face = FaceDetector.alignment_procedure(detected_face, left_eye, right_eye) - resp.append((detected_face, img_region)) + resp.append((detected_face, img_region, confidence)) return resp diff --git a/deepface/detectors/OpenCvWrapper.py b/deepface/detectors/OpenCvWrapper.py index b358f3e..fd04e00 100644 --- a/deepface/detectors/OpenCvWrapper.py +++ b/deepface/detectors/OpenCvWrapper.py @@ -41,17 +41,20 @@ def detect_face(detector, img, align = True): detected_face = None img_region = [0, 0, img.shape[0], img.shape[1]] + confidence = None faces = [] try: #faces = detector["face_detector"].detectMultiScale(img, 1.3, 5) - faces = detector["face_detector"].detectMultiScale(img, 1.1, 10) + + #note that, by design, opencv's haarcascade scores are >0 but not capped at 1 + faces, _, scores = detector["face_detector"].detectMultiScale3(img, 1.1, 10, outputRejectLevels = True) except: pass if len(faces) > 0: - for x,y,w,h in faces: + for (x,y,w,h), confidence in zip(faces, scores): detected_face = img[int(y):int(y+h), int(x):int(x+w)] if align: @@ -59,7 +62,7 @@ def detect_face(detector, img, align = True): img_region = [x, y, w, h] - resp.append((detected_face, img_region)) + resp.append((detected_face, img_region, confidence)) return resp diff --git a/deepface/detectors/RetinaFaceWrapper.py b/deepface/detectors/RetinaFaceWrapper.py index 7e81f8f..6c1a469 100644 --- a/deepface/detectors/RetinaFaceWrapper.py +++ b/deepface/detectors/RetinaFaceWrapper.py @@ -44,6 +44,7 @@ def detect_face(face_detector, img, align = True): x = facial_area[0] w = facial_area[2] - x img_region = [x, y, w, h] + confidence = identity["score"] #detected_face = img[int(y):int(y+h), int(x):int(x+w)] #opencv detected_face = img[facial_area[1]: facial_area[3], facial_area[0]: facial_area[2]] @@ -58,6 +59,6 @@ def detect_face(face_detector, img, align = True): detected_face = postprocess.alignment_procedure(detected_face, right_eye, left_eye, nose) - resp.append((detected_face, img_region)) + resp.append((detected_face, img_region, confidence)) return resp diff --git a/deepface/detectors/SsdWrapper.py b/deepface/detectors/SsdWrapper.py index 785a002..9c9846f 100644 --- a/deepface/detectors/SsdWrapper.py +++ b/deepface/detectors/SsdWrapper.py @@ -52,6 +52,7 @@ def detect_face(detector, img, align = True): detected_face = None img_region = [0, 0, img.shape[0], img.shape[1]] + confidence = None ssd_labels = ["img_id", "is_face", "confidence", "left", "top", "right", "bottom"] @@ -93,10 +94,11 @@ def detect_face(detector, img, align = True): detected_face = base_img[int(top*aspect_ratio_y):int(bottom*aspect_ratio_y), int(left*aspect_ratio_x):int(right*aspect_ratio_x)] img_region = [int(left*aspect_ratio_x), int(top*aspect_ratio_y), int(right*aspect_ratio_x) - int(left*aspect_ratio_x), int(bottom*aspect_ratio_y) - int(top*aspect_ratio_y)] + confidence = instance["confidence"] if align: detected_face = OpenCvWrapper.align_face(detector["eye_detector"], detected_face) - resp.append((detected_face, img_region)) + resp.append((detected_face, img_region, confidence)) return resp From f26bb45b0927c06dac1e31b48456b4baaacdb7d8 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Sun, 23 Oct 2022 17:23:58 +0100 Subject: [PATCH 4/6] verbose off anyway --- deepface/DeepFace.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index 3916566..81f1c22 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -367,7 +367,6 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = resp_objects = [] disable_option = (False if len(img_paths) > 1 else True) or not prog_bar - verbose = int(not disable_option) global_pbar = tqdm(range(0,len(img_paths)), desc='Analyzing', disable = disable_option) @@ -396,7 +395,7 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = 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) - emotion_predictions = models['emotion'].predict(img, verbose=verbose)[0,:] + emotion_predictions = models['emotion'].predict(img, verbose=0)[0,:] sum_of_predictions = emotion_predictions.sum() @@ -413,7 +412,7 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = 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) - age_predictions = models['age'].predict(img_224, verbose=verbose)[0,:] + age_predictions = models['age'].predict(img_224, verbose=0)[0,:] apparent_age = Age.findApparentAge(age_predictions) resp_obj["age"] = int(apparent_age) #int cast is for the exception - object of type 'float32' is not JSON serializable @@ -423,7 +422,7 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = 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) - gender_predictions = models['gender'].predict(img_224, verbose=verbose)[0,:] + gender_predictions = models['gender'].predict(img_224, verbose=0)[0,:] gender_labels = ["Woman", "Man"] resp_obj["gender"] = {} @@ -437,7 +436,7 @@ def analyze(img_path, actions = ('emotion', 'age', 'gender', 'race') , models = elif action == 'race': 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) #just emotion model expects grayscale images - race_predictions = models['race'].predict(img_224, verbose=verbose)[0,:] + race_predictions = models['race'].predict(img_224, verbose=0)[0,:] race_labels = ['asian', 'indian', 'black', 'white', 'middle eastern', 'latino hispanic'] sum_of_predictions = race_predictions.sum() @@ -766,7 +765,7 @@ def represent(img_path, model_name = 'VGG-Face', model = None, enforce_detection #--------------------------------- #represent - embedding = model.predict(img)[0].tolist() + embedding = model.predict(img, verbose=0)[0].tolist() return embedding From 51d10000c7d14ca0ecd3df255a31bc91503d7500 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Sun, 23 Oct 2022 17:55:10 +0100 Subject: [PATCH 5/6] verboss off for just tf models --- deepface/DeepFace.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index 81f1c22..9394857 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -765,7 +765,11 @@ def represent(img_path, model_name = 'VGG-Face', model = None, enforce_detection #--------------------------------- #represent - embedding = model.predict(img, verbose=0)[0].tolist() + if "keras" in str(type(model)): + embedding = model.predict(img, verbose=0)[0].tolist() + else: + #SFace is not a keras model and it has no verbose argument + embedding = model.predict(img)[0].tolist() return embedding From 11d3061c9fc8b52d6149315c3a3eaf99d067fac2 Mon Sep 17 00:00:00 2001 From: Sefik Ilkin Serengil Date: Sun, 23 Oct 2022 17:56:23 +0100 Subject: [PATCH 6/6] verbose condition --- deepface/DeepFace.py | 1 + 1 file changed, 1 insertion(+) diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index 9394857..f2f5fc9 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -766,6 +766,7 @@ def represent(img_path, model_name = 'VGG-Face', model = None, enforce_detection #represent if "keras" in str(type(model)): + #new tf versions show progress bar and it is annoying embedding = model.predict(img, verbose=0)[0].tolist() else: #SFace is not a keras model and it has no verbose argument