From b452bd26ac20b3f0226c36dfcb02dff0bc831817 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Tue, 23 May 2023 19:18:38 +0200 Subject: [PATCH 01/18] Add YOLOv8n-face, with confidence --- deepface/detectors/FaceDetector.py | 9 +++-- deepface/detectors/OpenCvWrapper.py | 21 +++++++----- deepface/detectors/Yolov8nfaceWrapper.py | 42 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 deepface/detectors/Yolov8nfaceWrapper.py diff --git a/deepface/detectors/FaceDetector.py b/deepface/detectors/FaceDetector.py index 367f588..3b02533 100644 --- a/deepface/detectors/FaceDetector.py +++ b/deepface/detectors/FaceDetector.py @@ -9,11 +9,11 @@ from deepface.detectors import ( MtcnnWrapper, RetinaFaceWrapper, MediapipeWrapper, + Yolov8nfaceWrapper, ) def build_model(detector_backend): - global face_detector_obj # singleton design pattern backends = { @@ -23,6 +23,7 @@ def build_model(detector_backend): "mtcnn": MtcnnWrapper.build_model, "retinaface": RetinaFaceWrapper.build_model, "mediapipe": MediapipeWrapper.build_model, + "yolov8n-face": Yolov8nfaceWrapper.build_model, } if not "face_detector_obj" in globals(): @@ -42,7 +43,6 @@ def build_model(detector_backend): def detect_face(face_detector, detector_backend, img, align=True): - obj = detect_faces(face_detector, detector_backend, img, align) if len(obj) > 0: @@ -50,12 +50,12 @@ def detect_face(face_detector, detector_backend, img, align=True): else: # len(obj) == 0 face = None region = [0, 0, img.shape[1], img.shape[0]] + confidence = 0 return face, region, confidence def detect_faces(face_detector, detector_backend, img, align=True): - backends = { "opencv": OpenCvWrapper.detect_face, "ssd": SsdWrapper.detect_face, @@ -63,6 +63,7 @@ def detect_faces(face_detector, detector_backend, img, align=True): "mtcnn": MtcnnWrapper.detect_face, "retinaface": RetinaFaceWrapper.detect_face, "mediapipe": MediapipeWrapper.detect_face, + "yolov8n-face": Yolov8nfaceWrapper.detect_face, } detect_face_fn = backends.get(detector_backend) @@ -76,7 +77,6 @@ def detect_faces(face_detector, detector_backend, img, align=True): def alignment_procedure(img, left_eye, right_eye): - # this function aligns given face in img based on left and right eye coordinates left_eye_x, left_eye_y = left_eye @@ -104,7 +104,6 @@ def alignment_procedure(img, left_eye, right_eye): # apply cosine rule if b != 0 and c != 0: # this multiplication causes division by zero in cos_a calculation - cos_a = (b * b + c * c - a * a) / (2 * b * c) angle = np.arccos(cos_a) # angle in radian angle = (angle * 180) / math.pi # radian to degree diff --git a/deepface/detectors/OpenCvWrapper.py b/deepface/detectors/OpenCvWrapper.py index cd0fc95..8a8025b 100644 --- a/deepface/detectors/OpenCvWrapper.py +++ b/deepface/detectors/OpenCvWrapper.py @@ -45,6 +45,7 @@ def detect_face(detector, img, align=True): img_region = [0, 0, img.shape[1], img.shape[0]] faces = [] + scores = [] try: # faces = detector["face_detector"].detectMultiScale(img, 1.3, 5) @@ -52,19 +53,21 @@ def detect_face(detector, img, align=True): faces, _, scores = detector["face_detector"].detectMultiScale3( img, 1.1, 10, outputRejectLevels=True ) - except: - pass + except Exception: # pylint: disable=broad-except + # except alone is too broad and will catch keyboard interrupts + import traceback - if len(faces) > 0: - for (x, y, w, h), confidence in zip(faces, scores): - detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] + print(traceback.format_exc()) - if align: - detected_face = align_face(detector["eye_detector"], detected_face) + for (x, y, w, h), confidence in zip(faces, scores): + detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] - img_region = [x, y, w, h] + if align: + detected_face = align_face(detector["eye_detector"], detected_face) - resp.append((detected_face, img_region, confidence)) + img_region = [x, y, w, h] + + resp.append((detected_face, img_region, confidence)) return resp diff --git a/deepface/detectors/Yolov8nfaceWrapper.py b/deepface/detectors/Yolov8nfaceWrapper.py new file mode 100644 index 0000000..3843e98 --- /dev/null +++ b/deepface/detectors/Yolov8nfaceWrapper.py @@ -0,0 +1,42 @@ +def build_model(): + import gdown + import os + + from ultralytics import YOLO + + from deepface.commons.functions import get_deepface_home + + weights_path = f"{get_deepface_home()}/.deepface/weights/yolov8n-face.pt" + + if not os.path.isfile(weights_path): + url = "https://drive.google.com/uc?id=1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb" + gdown.download(url, weights_path, quiet=False) + print("Downloaded YOLO model yolo8vn-face.pt") + + # return face_detector + return YOLO(weights_path) + + +def detect_face(face_detector, img, align=False): + resp = [] + confidence = -1 + detected_face = None + + # if align: + # raise NotImplementedError("`align` is not implemented for Yolov8Wrapper") + + results = face_detector.predict(img, verbose=False, show=True, conf=0.25)[0] + + for result in results: + x, y, w, h = result.boxes.xywh.tolist()[0] + confidence = result.boxes.conf.tolist()[0] + # print(f"Confidence: {confidence}, x: {x}, y: {y}, w: {w}, h: {h}") + + # print landmarks + print(result.keypoints.tolist()) + # change to top left corner, width, height + x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) + detected_face = img[y : y + h, x : x + w].copy() + resp.append((detected_face, [x, y, w, h], confidence)) + + return resp From ed38c03eae643d40d7e212b84c56453f5ad7fe05 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Tue, 23 May 2023 19:30:54 +0200 Subject: [PATCH 02/18] Add eyes landmarks. --- deepface/detectors/Yolov8nfaceWrapper.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/deepface/detectors/Yolov8nfaceWrapper.py b/deepface/detectors/Yolov8nfaceWrapper.py index 3843e98..9925e08 100644 --- a/deepface/detectors/Yolov8nfaceWrapper.py +++ b/deepface/detectors/Yolov8nfaceWrapper.py @@ -22,9 +22,6 @@ def detect_face(face_detector, img, align=False): confidence = -1 detected_face = None - # if align: - # raise NotImplementedError("`align` is not implemented for Yolov8Wrapper") - results = face_detector.predict(img, verbose=False, show=True, conf=0.25)[0] for result in results: @@ -33,10 +30,22 @@ def detect_face(face_detector, img, align=False): # print(f"Confidence: {confidence}, x: {x}, y: {y}, w: {w}, h: {h}") # print landmarks - print(result.keypoints.tolist()) + landmarks = result.keypoints.tolist() + left_eye, right_eye = landmarks[0], landmarks[1] + # print(result.keypoints.tolist()) + # print(f"Left eye: {left_eye}, right eye: {right_eye}") + + # add eyes landmarks to img + import cv2 + + img = cv2.circle(img, (int(left_eye[0]), int(left_eye[1])), 2, (0, 0, 255), 2) + img = cv2.circle(img, (int(right_eye[0]), int(right_eye[1])), 2, (0, 255, 0), 2) # change to top left corner, width, height x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) detected_face = img[y : y + h, x : x + w].copy() + + # if align: + # raise NotImplementedError("`align` is not implemented for Yolov8Wrapper") resp.append((detected_face, [x, y, w, h], confidence)) return resp From 1b33865e05423cfdd1a43743950f842609639d3a Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Tue, 23 May 2023 20:51:35 +0200 Subject: [PATCH 03/18] Add alignment. --- deepface/detectors/Yolov8nfaceWrapper.py | 25 +++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/deepface/detectors/Yolov8nfaceWrapper.py b/deepface/detectors/Yolov8nfaceWrapper.py index 9925e08..1ff7909 100644 --- a/deepface/detectors/Yolov8nfaceWrapper.py +++ b/deepface/detectors/Yolov8nfaceWrapper.py @@ -1,3 +1,6 @@ +from deepface.detectors import FaceDetector + + def build_model(): import gdown import os @@ -19,8 +22,6 @@ def build_model(): def detect_face(face_detector, img, align=False): resp = [] - confidence = -1 - detected_face = None results = face_detector.predict(img, verbose=False, show=True, conf=0.25)[0] @@ -30,22 +31,28 @@ def detect_face(face_detector, img, align=False): # print(f"Confidence: {confidence}, x: {x}, y: {y}, w: {w}, h: {h}") # print landmarks - landmarks = result.keypoints.tolist() - left_eye, right_eye = landmarks[0], landmarks[1] + # print(result.keypoints.tolist()) # print(f"Left eye: {left_eye}, right eye: {right_eye}") # add eyes landmarks to img - import cv2 + # import cv2 - img = cv2.circle(img, (int(left_eye[0]), int(left_eye[1])), 2, (0, 0, 255), 2) - img = cv2.circle(img, (int(right_eye[0]), int(right_eye[1])), 2, (0, 255, 0), 2) + # img = cv2.circle(img, (int(left_eye[0]), int(left_eye[1])), 2, (0, 0, 255), 2) + # img = cv2.circle(img, (int(right_eye[0]), int(right_eye[1])), 2, (0, 255, 0), 2) # change to top left corner, width, height x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) detected_face = img[y : y + h, x : x + w].copy() - # if align: - # raise NotImplementedError("`align` is not implemented for Yolov8Wrapper") + if align: + left_eye, right_eye, _, _, _ = result.keypoints.tolist() + # Check the landmarks confidence before alignment + # print(f"Left eye: {left_eye[2]}, right eye: {right_eye[2]}") + if left_eye[2] > 0.5 and right_eye[2] > 0.5: + # print("Aligning face") + # print(left_eye[:2], right_eye[:2]) + detected_face = FaceDetector.alignment_procedure(detected_face, left_eye[:2], right_eye[:2]) + resp.append((detected_face, [x, y, w, h], confidence)) return resp From b08695ac39ad837176583537d412fdacf2c57087 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Tue, 23 May 2023 20:53:29 +0200 Subject: [PATCH 04/18] Clean the new wrapper. --- deepface/detectors/Yolov8nfaceWrapper.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/deepface/detectors/Yolov8nfaceWrapper.py b/deepface/detectors/Yolov8nfaceWrapper.py index 1ff7909..29e0d60 100644 --- a/deepface/detectors/Yolov8nfaceWrapper.py +++ b/deepface/detectors/Yolov8nfaceWrapper.py @@ -28,30 +28,18 @@ def detect_face(face_detector, img, align=False): for result in results: x, y, w, h = result.boxes.xywh.tolist()[0] confidence = result.boxes.conf.tolist()[0] - # print(f"Confidence: {confidence}, x: {x}, y: {y}, w: {w}, h: {h}") - # print landmarks - - # print(result.keypoints.tolist()) - # print(f"Left eye: {left_eye}, right eye: {right_eye}") - - # add eyes landmarks to img - # import cv2 - - # img = cv2.circle(img, (int(left_eye[0]), int(left_eye[1])), 2, (0, 0, 255), 2) - # img = cv2.circle(img, (int(right_eye[0]), int(right_eye[1])), 2, (0, 255, 0), 2) - # change to top left corner, width, height x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) detected_face = img[y : y + h, x : x + w].copy() if align: + # Extract landmarks left_eye, right_eye, _, _, _ = result.keypoints.tolist() # Check the landmarks confidence before alignment - # print(f"Left eye: {left_eye[2]}, right eye: {right_eye[2]}") if left_eye[2] > 0.5 and right_eye[2] > 0.5: - # print("Aligning face") - # print(left_eye[:2], right_eye[:2]) - detected_face = FaceDetector.alignment_procedure(detected_face, left_eye[:2], right_eye[:2]) + detected_face = FaceDetector.alignment_procedure( + detected_face, left_eye[:2], right_eye[:2] + ) resp.append((detected_face, [x, y, w, h], confidence)) From 659633f2867c4712981954123238ca09fd8fc5d9 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Tue, 23 May 2023 23:00:49 +0200 Subject: [PATCH 05/18] All available YOLOv8-face models --- deepface/detectors/FaceDetector.py | 10 ++-- deepface/detectors/Yolov8faceWrapper.py | 64 ++++++++++++++++++++++++ deepface/detectors/Yolov8nfaceWrapper.py | 46 ----------------- 3 files changed, 71 insertions(+), 49 deletions(-) create mode 100644 deepface/detectors/Yolov8faceWrapper.py delete mode 100644 deepface/detectors/Yolov8nfaceWrapper.py diff --git a/deepface/detectors/FaceDetector.py b/deepface/detectors/FaceDetector.py index 3b02533..05f6cbc 100644 --- a/deepface/detectors/FaceDetector.py +++ b/deepface/detectors/FaceDetector.py @@ -9,7 +9,7 @@ from deepface.detectors import ( MtcnnWrapper, RetinaFaceWrapper, MediapipeWrapper, - Yolov8nfaceWrapper, + Yolov8faceWrapper, ) @@ -23,7 +23,9 @@ def build_model(detector_backend): "mtcnn": MtcnnWrapper.build_model, "retinaface": RetinaFaceWrapper.build_model, "mediapipe": MediapipeWrapper.build_model, - "yolov8n-face": Yolov8nfaceWrapper.build_model, + "yolov8-lite-t": Yolov8faceWrapper.build_model("yolov8-lite-t"), + "yolov8-lite-s": Yolov8faceWrapper.build_model("yolov8-lite-s"), + "yolov8n": Yolov8faceWrapper.build_model("yolov8n"), } if not "face_detector_obj" in globals(): @@ -63,7 +65,9 @@ def detect_faces(face_detector, detector_backend, img, align=True): "mtcnn": MtcnnWrapper.detect_face, "retinaface": RetinaFaceWrapper.detect_face, "mediapipe": MediapipeWrapper.detect_face, - "yolov8n-face": Yolov8nfaceWrapper.detect_face, + "yolov8-lite-t": Yolov8faceWrapper.detect_face, + "yolov8-lite-s": Yolov8faceWrapper.detect_face, + "yolov8n": Yolov8faceWrapper.detect_face, } detect_face_fn = backends.get(detector_backend) diff --git a/deepface/detectors/Yolov8faceWrapper.py b/deepface/detectors/Yolov8faceWrapper.py new file mode 100644 index 0000000..fba24d4 --- /dev/null +++ b/deepface/detectors/Yolov8faceWrapper.py @@ -0,0 +1,64 @@ +from deepface.detectors import FaceDetector + +PATHS = { + "yolov8-lite-t": "/.deepface/weights/yolov8-lite-t.pt", + "yolov8-lite-s": "/.deepface/weights/yolov8-lite-s.pt", + "yolov8n": "/.deepface/weights/yolov8n-face.pt", +} + +BASE_URL = "https://drive.google.com/uc?id=" + +IDS = { + "yolov8-lite-t": "1vFMGW8xtRVo9bfC9yJVWWGY7vVxbLh94", + "yolov8-lite-s": "1ckpBT8KfwURTvTm5pa-cMC89A0V5jbaq", + "yolov8n": "1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb", +} + + +def build_model(model: str): + """Function factory for YOLO models""" + from deepface.commons.functions import get_deepface_home + + func_weights_path = f"{get_deepface_home()}{PATHS[model]}" + func_url = f"{BASE_URL}{IDS[model]}" + + def _build_model(weights_path: str = func_weights_path, url: str = func_url): + import gdown + import os + + from ultralytics import YOLO + + if not os.path.isfile(weights_path): + gdown.download(url, weights_path, quiet=False) + print(f"Downloaded YOLO model {os.path.basename(PATHS[model])}") + + # return face_detector + return YOLO(weights_path) + + return _build_model + + +def detect_face(face_detector, img, align=False): + resp = [] + + results = face_detector.predict(img, verbose=False, show=True, conf=0.25)[0] + + for result in results: + x, y, w, h = result.boxes.xywh.tolist()[0] + confidence = result.boxes.conf.tolist()[0] + + x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) + detected_face = img[y : y + h, x : x + w].copy() + + if align: + # Extract landmarks + left_eye, right_eye, _, _, _ = result.keypoints.tolist() + # Check the landmarks confidence before alignment + if left_eye[2] > 0.5 and right_eye[2] > 0.5: + detected_face = FaceDetector.alignment_procedure( + detected_face, left_eye[:2], right_eye[:2] + ) + + resp.append((detected_face, [x, y, w, h], confidence)) + + return resp diff --git a/deepface/detectors/Yolov8nfaceWrapper.py b/deepface/detectors/Yolov8nfaceWrapper.py deleted file mode 100644 index 29e0d60..0000000 --- a/deepface/detectors/Yolov8nfaceWrapper.py +++ /dev/null @@ -1,46 +0,0 @@ -from deepface.detectors import FaceDetector - - -def build_model(): - import gdown - import os - - from ultralytics import YOLO - - from deepface.commons.functions import get_deepface_home - - weights_path = f"{get_deepface_home()}/.deepface/weights/yolov8n-face.pt" - - if not os.path.isfile(weights_path): - url = "https://drive.google.com/uc?id=1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb" - gdown.download(url, weights_path, quiet=False) - print("Downloaded YOLO model yolo8vn-face.pt") - - # return face_detector - return YOLO(weights_path) - - -def detect_face(face_detector, img, align=False): - resp = [] - - results = face_detector.predict(img, verbose=False, show=True, conf=0.25)[0] - - for result in results: - x, y, w, h = result.boxes.xywh.tolist()[0] - confidence = result.boxes.conf.tolist()[0] - - x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) - detected_face = img[y : y + h, x : x + w].copy() - - if align: - # Extract landmarks - left_eye, right_eye, _, _, _ = result.keypoints.tolist() - # Check the landmarks confidence before alignment - if left_eye[2] > 0.5 and right_eye[2] > 0.5: - detected_face = FaceDetector.alignment_procedure( - detected_face, left_eye[:2], right_eye[:2] - ) - - resp.append((detected_face, [x, y, w, h], confidence)) - - return resp From b32c31aea32654ee63c6be724d81e21c500031c3 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Tue, 23 May 2023 23:01:34 +0200 Subject: [PATCH 06/18] Remove broken models. --- deepface/detectors/FaceDetector.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/deepface/detectors/FaceDetector.py b/deepface/detectors/FaceDetector.py index 05f6cbc..631ba8f 100644 --- a/deepface/detectors/FaceDetector.py +++ b/deepface/detectors/FaceDetector.py @@ -23,8 +23,6 @@ def build_model(detector_backend): "mtcnn": MtcnnWrapper.build_model, "retinaface": RetinaFaceWrapper.build_model, "mediapipe": MediapipeWrapper.build_model, - "yolov8-lite-t": Yolov8faceWrapper.build_model("yolov8-lite-t"), - "yolov8-lite-s": Yolov8faceWrapper.build_model("yolov8-lite-s"), "yolov8n": Yolov8faceWrapper.build_model("yolov8n"), } @@ -65,8 +63,6 @@ def detect_faces(face_detector, detector_backend, img, align=True): "mtcnn": MtcnnWrapper.detect_face, "retinaface": RetinaFaceWrapper.detect_face, "mediapipe": MediapipeWrapper.detect_face, - "yolov8-lite-t": Yolov8faceWrapper.detect_face, - "yolov8-lite-s": Yolov8faceWrapper.detect_face, "yolov8n": Yolov8faceWrapper.detect_face, } From 6c06996686a6a544539907bea501ac91addb868d Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Tue, 23 May 2023 23:17:55 +0200 Subject: [PATCH 07/18] Remove "show" from the YOLOv8 detector. --- deepface/detectors/Yolov8faceWrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepface/detectors/Yolov8faceWrapper.py b/deepface/detectors/Yolov8faceWrapper.py index fba24d4..aefa3f3 100644 --- a/deepface/detectors/Yolov8faceWrapper.py +++ b/deepface/detectors/Yolov8faceWrapper.py @@ -41,7 +41,7 @@ def build_model(model: str): def detect_face(face_detector, img, align=False): resp = [] - results = face_detector.predict(img, verbose=False, show=True, conf=0.25)[0] + results = face_detector.predict(img, verbose=False, show=False, conf=0.25)[0] for result in results: x, y, w, h = result.boxes.xywh.tolist()[0] From a380cfffbbc79a724fa995c9760babdc7f865831 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Mon, 5 Jun 2023 13:47:55 +0200 Subject: [PATCH 08/18] Remove unwanted changes, and add comments. --- .vscode/settings.json | 4 +-- README.md | 5 ++-- deepface/DeepFace.py | 36 ++++++++++++------------- deepface/detectors/FaceDetector.py | 3 +++ deepface/detectors/OpenCvWrapper.py | 9 +++++-- deepface/detectors/Yolov8faceWrapper.py | 12 ++++++++- 6 files changed, 42 insertions(+), 27 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e223550..f5d9a83 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,5 @@ "python.formatting.provider": "black", "python.formatting.blackArgs": ["--line-length=100"], "editor.fontWeight": "normal", - "python.analysis.extraPaths": ["./deepface"], - "stylelint.autoFixOnSave": false, - "standard.autoFixOnSave": false + "python.analysis.extraPaths": ["./deepface"] } diff --git a/README.md b/README.md index 9e59f40..4c1c0cd 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Age model got ± 4.65 MAE; gender model got 97.44% accuracy, 96.29% precision an **Face Detectors** - [`Demo`](https://youtu.be/GZ2p2hj2H5k) -Face detection and alignment are important early stages of a modern face recognition pipeline. Experiments show that just alignment increases the face recognition accuracy almost 1%. [`OpenCV`](https://sefiks.com/2020/02/23/face-alignment-for-face-recognition-in-python-within-opencv/), [`SSD`](https://sefiks.com/2020/08/25/deep-face-detection-with-opencv-in-python/), [`Dlib`](https://sefiks.com/2020/07/11/face-recognition-with-dlib-in-python/), [`MTCNN`](https://sefiks.com/2020/09/09/deep-face-detection-with-mtcnn-in-python/), [`RetinaFace`](https://sefiks.com/2021/04/27/deep-face-detection-with-retinaface-in-python/) and [`MediaPipe`](https://sefiks.com/2022/01/14/deep-face-detection-with-mediapipe/) detectors are wrapped in deepface. +Face detection and alignment are important early stages of a modern face recognition pipeline. Experiments show that just alignment increases the face recognition accuracy almost 1%. [`OpenCV`](https://sefiks.com/2020/02/23/face-alignment-for-face-recognition-in-python-within-opencv/), [`SSD`](https://sefiks.com/2020/08/25/deep-face-detection-with-opencv-in-python/), [`Dlib`](https://sefiks.com/2020/07/11/face-recognition-with-dlib-in-python/), [`MTCNN`](https://sefiks.com/2020/09/09/deep-face-detection-with-mtcnn-in-python/), [`RetinaFace`](https://sefiks.com/2021/04/27/deep-face-detection-with-retinaface-in-python/), [`MediaPipe`](https://sefiks.com/2022/01/14/deep-face-detection-with-mediapipe/) and [`YOLOv8 Face`](https://github.com/derronqi/yolov8-face) detectors are wrapped in deepface.

@@ -207,7 +207,8 @@ backends = [ 'dlib', 'mtcnn', 'retinaface', - 'mediapipe' + 'mediapipe', + 'yolov8n', ] #face verification diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index a68bd01..2f28503 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -41,6 +41,7 @@ if tf_version == 2: def build_model(model_name): + """ This function builds a deepface model Parameters: @@ -95,6 +96,7 @@ def verify( align=True, normalization="base", ): + """ This function verifies an image pair is same person or different persons. In the background, verification function represents facial images as vectors and then calculates the similarity @@ -116,7 +118,7 @@ def verify( This might be convenient for low resolution images. detector_backend (string): set face detector backend to opencv, retinaface, mtcnn, ssd, - dlib or mediapipe + dlib, mediapipe or yolov8n. align (boolean): alignment according to the eye positions. @@ -233,8 +235,9 @@ def analyze( align=True, silent=False, ): + """ - This function analyze facial attributes including age, gender, emotion and race. + This function analyzes facial attributes including age, gender, emotion and race. In the background, analysis function builds convolutional neural network models to classify age, gender, emotion and race of the input image. @@ -251,7 +254,7 @@ def analyze( resolution images. detector_backend (string): set face detector backend to opencv, retinaface, mtcnn, ssd, - dlib or mediapipe. + dlib, mediapipe or yolov8n. align (boolean): alignment according to the eye positions. @@ -297,16 +300,6 @@ def analyze( actions = (actions,) actions = list(actions) - - if not actions: - raise ValueError("`actions` must be a list of strings.") - - for action in actions: - if action not in ("emotion", "age", "gender", "race"): - raise ValueError( - f"Invalid action passed ({action})). " - "Valid actions are `emotion`, `age`, `gender`, `race`." - ) # --------------------------------- # build models models = {} @@ -405,6 +398,7 @@ def find( normalization="base", silent=False, ): + """ This function applies verification several times and find the identities in a database @@ -427,10 +421,10 @@ def find( resolution images. detector_backend (string): set face detector backend to opencv, retinaface, mtcnn, ssd, - dlib or mediapipe + dlib, mediapipe or yolov8n. align (boolean): alignment according to the eye positions. - + normalization (string): normalize the input image before feeding to model silent (boolean): disable some logging and progress bars @@ -454,6 +448,7 @@ def find( file_name = file_name.replace("-", "_").lower() if path.exists(db_path + "/" + file_name): + if not silent: print( f"WARNING: Representations for images in {db_path} folder were previously stored" @@ -621,6 +616,7 @@ def represent( align=True, normalization="base", ): + """ This function represents facial images as vectors. The function uses convolutional neural networks models to generate vector embeddings. @@ -638,7 +634,7 @@ def represent( This might be convenient for low resolution images. detector_backend (string): set face detector backend to opencv, retinaface, mtcnn, ssd, - dlib or mediapipe + dlib, mediapipe or yolov8n. align (boolean): alignment according to the eye positions. @@ -714,6 +710,7 @@ def stream( time_threshold=5, frame_threshold=5, ): + """ This function applies real time face recognition and facial attribute analysis @@ -723,7 +720,7 @@ def stream( model_name (string): VGG-Face, Facenet, Facenet512, OpenFace, DeepFace, DeepID, Dlib, ArcFace, SFace - detector_backend (string): opencv, retinaface, mtcnn, ssd, dlib or mediapipe + detector_backend (string): opencv, retinaface, mtcnn, ssd, dlib, mediapipe or yolov8n. distance_metric (string): cosine, euclidean, euclidean_l2 @@ -768,6 +765,7 @@ def extract_faces( align=True, grayscale=False, ): + """ This function applies pre-processing stages of a face recognition pipeline including detection and alignment @@ -832,7 +830,7 @@ def detectFace( ): """ Deprecated function. Use extract_faces for same functionality. - + This function applies pre-processing stages of a face recognition pipeline including detection and alignment @@ -857,7 +855,7 @@ def detectFace( Returns: detected and aligned face as numpy array - + """ print("⚠️ Function detectFace is deprecated. Use extract_faces instead.") face_objs = extract_faces( diff --git a/deepface/detectors/FaceDetector.py b/deepface/detectors/FaceDetector.py index 631ba8f..dffa854 100644 --- a/deepface/detectors/FaceDetector.py +++ b/deepface/detectors/FaceDetector.py @@ -47,6 +47,9 @@ def detect_face(face_detector, detector_backend, img, align=True): if len(obj) > 0: face, region, confidence = obj[0] # discard multiple faces + + # If no face detected, return, set face to None, + # image region to full image, confidence to 0 else: # len(obj) == 0 face = None region = [0, 0, img.shape[1], img.shape[0]] diff --git a/deepface/detectors/OpenCvWrapper.py b/deepface/detectors/OpenCvWrapper.py index 8a8025b..442b452 100644 --- a/deepface/detectors/OpenCvWrapper.py +++ b/deepface/detectors/OpenCvWrapper.py @@ -44,6 +44,7 @@ def detect_face(detector, img, align=True): detected_face = None img_region = [0, 0, img.shape[1], img.shape[0]] + # Initialize faces and scores to empty lists faces = [] scores = [] try: @@ -53,12 +54,16 @@ def detect_face(detector, img, align=True): faces, _, scores = detector["face_detector"].detectMultiScale3( img, 1.1, 10, outputRejectLevels=True ) + + # except alone is too broad and will catch keyboard interrupts + # Exception should be changed to something more specific in the future except Exception: # pylint: disable=broad-except - # except alone is too broad and will catch keyboard interrupts import traceback print(traceback.format_exc()) - + + # For each face and associated score, append face, + # bounding box, and score to resp for (x, y, w, h), confidence in zip(faces, scores): detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] diff --git a/deepface/detectors/Yolov8faceWrapper.py b/deepface/detectors/Yolov8faceWrapper.py index aefa3f3..49a3225 100644 --- a/deepface/detectors/Yolov8faceWrapper.py +++ b/deepface/detectors/Yolov8faceWrapper.py @@ -1,13 +1,16 @@ from deepface.detectors import FaceDetector +# Models names and paths PATHS = { "yolov8-lite-t": "/.deepface/weights/yolov8-lite-t.pt", "yolov8-lite-s": "/.deepface/weights/yolov8-lite-s.pt", "yolov8n": "/.deepface/weights/yolov8n-face.pt", } +# Google Drive base URL BASE_URL = "https://drive.google.com/uc?id=" +# Models' Google Drive IDs IDS = { "yolov8-lite-t": "1vFMGW8xtRVo9bfC9yJVWWGY7vVxbLh94", "yolov8-lite-s": "1ckpBT8KfwURTvTm5pa-cMC89A0V5jbaq", @@ -19,20 +22,24 @@ def build_model(model: str): """Function factory for YOLO models""" from deepface.commons.functions import get_deepface_home + # Get model's weights path and Google Drive URL func_weights_path = f"{get_deepface_home()}{PATHS[model]}" func_url = f"{BASE_URL}{IDS[model]}" + # Define function to build the model def _build_model(weights_path: str = func_weights_path, url: str = func_url): import gdown import os + # Import the Ultralytics YOLO model from ultralytics import YOLO + # Download the model's weights if they don't exist if not os.path.isfile(weights_path): gdown.download(url, weights_path, quiet=False) print(f"Downloaded YOLO model {os.path.basename(PATHS[model])}") - # return face_detector + # Return face_detector return YOLO(weights_path) return _build_model @@ -41,9 +48,12 @@ def build_model(model: str): def detect_face(face_detector, img, align=False): resp = [] + # Detect faces results = face_detector.predict(img, verbose=False, show=False, conf=0.25)[0] + # For each face, extract the bounding box, the landmarks and confidence for result in results: + # Extract the bounding box and the confidence x, y, w, h = result.boxes.xywh.tolist()[0] confidence = result.boxes.conf.tolist()[0] From f88a560f217eb9f5f550d36bff38a8701d08e00e Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Mon, 5 Jun 2023 13:54:40 +0200 Subject: [PATCH 09/18] Fix linting errors. --- deepface/DeepFace.py | 6 +++--- deepface/detectors/OpenCvWrapper.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index 2f28503..5961b7b 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -424,7 +424,7 @@ def find( dlib, mediapipe or yolov8n. align (boolean): alignment according to the eye positions. - + normalization (string): normalize the input image before feeding to model silent (boolean): disable some logging and progress bars @@ -830,7 +830,7 @@ def detectFace( ): """ Deprecated function. Use extract_faces for same functionality. - + This function applies pre-processing stages of a face recognition pipeline including detection and alignment @@ -855,7 +855,7 @@ def detectFace( Returns: detected and aligned face as numpy array - + """ print("⚠️ Function detectFace is deprecated. Use extract_faces instead.") face_objs = extract_faces( diff --git a/deepface/detectors/OpenCvWrapper.py b/deepface/detectors/OpenCvWrapper.py index 442b452..655b00a 100644 --- a/deepface/detectors/OpenCvWrapper.py +++ b/deepface/detectors/OpenCvWrapper.py @@ -61,7 +61,7 @@ def detect_face(detector, img, align=True): import traceback print(traceback.format_exc()) - + # For each face and associated score, append face, # bounding box, and score to resp for (x, y, w, h), confidence in zip(faces, scores): From 0e9d7b7915beba73c523eb312c1d3839debd4f97 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Mon, 5 Jun 2023 13:56:47 +0200 Subject: [PATCH 10/18] Grammar. --- deepface/detectors/FaceDetector.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deepface/detectors/FaceDetector.py b/deepface/detectors/FaceDetector.py index dffa854..c37f546 100644 --- a/deepface/detectors/FaceDetector.py +++ b/deepface/detectors/FaceDetector.py @@ -48,8 +48,8 @@ def detect_face(face_detector, detector_backend, img, align=True): if len(obj) > 0: face, region, confidence = obj[0] # discard multiple faces - # If no face detected, return, set face to None, - # image region to full image, confidence to 0 + # If no face is detected, set face to None, + # image region to full image, and confidence to 0. else: # len(obj) == 0 face = None region = [0, 0, img.shape[1], img.shape[0]] From 16d37711d5d31daf152c0dbef7496472fa6aa1f2 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Fri, 23 Jun 2023 20:33:47 +0200 Subject: [PATCH 11/18] Remove unwanted line returns. --- deepface/DeepFace.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index 3c33b41..2167ab9 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -96,7 +96,6 @@ def verify( align=True, normalization="base", ): - """ This function verifies an image pair is same person or different persons. In the background, verification function represents facial images as vectors and then calculates the similarity @@ -235,7 +234,6 @@ def analyze( align=True, silent=False, ): - """ This function analyzes facial attributes including age, gender, emotion and race. In the background, analysis function builds convolutional neural network models to @@ -410,7 +408,6 @@ def find( normalization="base", silent=False, ): - """ This function applies verification several times and find the identities in a database @@ -628,7 +625,6 @@ def represent( align=True, normalization="base", ): - """ This function represents facial images as vectors. The function uses convolutional neural networks models to generate vector embeddings. @@ -722,7 +718,6 @@ def stream( time_threshold=5, frame_threshold=5, ): - """ This function applies real time face recognition and facial attribute analysis @@ -777,7 +772,6 @@ def extract_faces( align=True, grayscale=False, ): - """ This function applies pre-processing stages of a face recognition pipeline including detection and alignment From 0c50eb71782bec2cd99a74ead69f937be80ec653 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Fri, 23 Jun 2023 20:34:51 +0200 Subject: [PATCH 12/18] Remove unwanted line return. --- deepface/DeepFace.py | 1 - 1 file changed, 1 deletion(-) diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index 2167ab9..d7cd1c6 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -41,7 +41,6 @@ if tf_version == 2: def build_model(model_name): - """ This function builds a deepface model Parameters: From 205eb0e23d65bdff98821aa10f518e7948520404 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Fri, 23 Jun 2023 20:41:29 +0200 Subject: [PATCH 13/18] Revert OpenCvWrapper.py --- deepface/detectors/OpenCvWrapper.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/deepface/detectors/OpenCvWrapper.py b/deepface/detectors/OpenCvWrapper.py index 655b00a..cd0fc95 100644 --- a/deepface/detectors/OpenCvWrapper.py +++ b/deepface/detectors/OpenCvWrapper.py @@ -44,9 +44,7 @@ def detect_face(detector, img, align=True): detected_face = None img_region = [0, 0, img.shape[1], img.shape[0]] - # Initialize faces and scores to empty lists faces = [] - scores = [] try: # faces = detector["face_detector"].detectMultiScale(img, 1.3, 5) @@ -54,25 +52,19 @@ def detect_face(detector, img, align=True): faces, _, scores = detector["face_detector"].detectMultiScale3( img, 1.1, 10, outputRejectLevels=True ) + except: + pass - # except alone is too broad and will catch keyboard interrupts - # Exception should be changed to something more specific in the future - except Exception: # pylint: disable=broad-except - import traceback + if len(faces) > 0: + for (x, y, w, h), confidence in zip(faces, scores): + detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] - print(traceback.format_exc()) + if align: + detected_face = align_face(detector["eye_detector"], detected_face) - # For each face and associated score, append face, - # bounding box, and score to resp - for (x, y, w, h), confidence in zip(faces, scores): - detected_face = img[int(y) : int(y + h), int(x) : int(x + w)] + img_region = [x, y, w, h] - if align: - detected_face = align_face(detector["eye_detector"], detected_face) - - img_region = [x, y, w, h] - - resp.append((detected_face, img_region, confidence)) + resp.append((detected_face, img_region, confidence)) return resp From 05106130041d2e1ec5a816da80b95dd5feb7d420 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Fri, 23 Jun 2023 20:45:58 +0200 Subject: [PATCH 14/18] Comment out broken models. --- deepface/detectors/Yolov8faceWrapper.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/deepface/detectors/Yolov8faceWrapper.py b/deepface/detectors/Yolov8faceWrapper.py index 49a3225..18c9ede 100644 --- a/deepface/detectors/Yolov8faceWrapper.py +++ b/deepface/detectors/Yolov8faceWrapper.py @@ -2,8 +2,11 @@ from deepface.detectors import FaceDetector # Models names and paths PATHS = { - "yolov8-lite-t": "/.deepface/weights/yolov8-lite-t.pt", - "yolov8-lite-s": "/.deepface/weights/yolov8-lite-s.pt", + # The commented models are broken, their weights are available + # but the models themselves are not working + # See https://github.com/derronqi/yolov8-face/issues/3 for details + # "yolov8-lite-t": "/.deepface/weights/yolov8-lite-t.pt", + # "yolov8-lite-s": "/.deepface/weights/yolov8-lite-s.pt", "yolov8n": "/.deepface/weights/yolov8n-face.pt", } @@ -12,8 +15,11 @@ BASE_URL = "https://drive.google.com/uc?id=" # Models' Google Drive IDs IDS = { - "yolov8-lite-t": "1vFMGW8xtRVo9bfC9yJVWWGY7vVxbLh94", - "yolov8-lite-s": "1ckpBT8KfwURTvTm5pa-cMC89A0V5jbaq", + # The commented models are broken, their weights are available + # but the models themselves are not working + # See https://github.com/derronqi/yolov8-face/issues/3 for details + # "yolov8-lite-t": "1vFMGW8xtRVo9bfC9yJVWWGY7vVxbLh94", + # "yolov8-lite-s": "1ckpBT8KfwURTvTm5pa-cMC89A0V5jbaq", "yolov8n": "1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb", } @@ -49,7 +55,8 @@ def detect_face(face_detector, img, align=False): resp = [] # Detect faces - results = face_detector.predict(img, verbose=False, show=False, conf=0.25)[0] + results = face_detector.predict( + img, verbose=False, show=False, conf=0.25)[0] # For each face, extract the bounding box, the landmarks and confidence for result in results: @@ -58,7 +65,7 @@ def detect_face(face_detector, img, align=False): confidence = result.boxes.conf.tolist()[0] x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) - detected_face = img[y : y + h, x : x + w].copy() + detected_face = img[y: y + h, x: x + w].copy() if align: # Extract landmarks From 776bd1170772f0003e8c6ee30a63a877a223f017 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Fri, 23 Jun 2023 20:49:05 +0200 Subject: [PATCH 15/18] Remove broken models. --- deepface/detectors/Yolov8faceWrapper.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/deepface/detectors/Yolov8faceWrapper.py b/deepface/detectors/Yolov8faceWrapper.py index 18c9ede..0e9142d 100644 --- a/deepface/detectors/Yolov8faceWrapper.py +++ b/deepface/detectors/Yolov8faceWrapper.py @@ -2,11 +2,6 @@ from deepface.detectors import FaceDetector # Models names and paths PATHS = { - # The commented models are broken, their weights are available - # but the models themselves are not working - # See https://github.com/derronqi/yolov8-face/issues/3 for details - # "yolov8-lite-t": "/.deepface/weights/yolov8-lite-t.pt", - # "yolov8-lite-s": "/.deepface/weights/yolov8-lite-s.pt", "yolov8n": "/.deepface/weights/yolov8n-face.pt", } @@ -15,11 +10,6 @@ BASE_URL = "https://drive.google.com/uc?id=" # Models' Google Drive IDs IDS = { - # The commented models are broken, their weights are available - # but the models themselves are not working - # See https://github.com/derronqi/yolov8-face/issues/3 for details - # "yolov8-lite-t": "1vFMGW8xtRVo9bfC9yJVWWGY7vVxbLh94", - # "yolov8-lite-s": "1ckpBT8KfwURTvTm5pa-cMC89A0V5jbaq", "yolov8n": "1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb", } From dcf94150f24617289a7a7ff76dba1d4445f55528 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Fri, 23 Jun 2023 23:31:50 +0200 Subject: [PATCH 16/18] Refactoring and requirements. --- deepface/detectors/FaceDetector.py | 6 +-- deepface/detectors/YoloWrapper.py | 61 +++++++++++++++++++++ deepface/detectors/Yolov8faceWrapper.py | 71 ------------------------- requirements_additional.txt | 3 +- 4 files changed, 66 insertions(+), 75 deletions(-) create mode 100644 deepface/detectors/YoloWrapper.py delete mode 100644 deepface/detectors/Yolov8faceWrapper.py diff --git a/deepface/detectors/FaceDetector.py b/deepface/detectors/FaceDetector.py index c37f546..f43841b 100644 --- a/deepface/detectors/FaceDetector.py +++ b/deepface/detectors/FaceDetector.py @@ -9,7 +9,7 @@ from deepface.detectors import ( MtcnnWrapper, RetinaFaceWrapper, MediapipeWrapper, - Yolov8faceWrapper, + YoloWrapper, ) @@ -23,7 +23,7 @@ def build_model(detector_backend): "mtcnn": MtcnnWrapper.build_model, "retinaface": RetinaFaceWrapper.build_model, "mediapipe": MediapipeWrapper.build_model, - "yolov8n": Yolov8faceWrapper.build_model("yolov8n"), + "yolov8n": YoloWrapper.build_model, } if not "face_detector_obj" in globals(): @@ -66,7 +66,7 @@ def detect_faces(face_detector, detector_backend, img, align=True): "mtcnn": MtcnnWrapper.detect_face, "retinaface": RetinaFaceWrapper.detect_face, "mediapipe": MediapipeWrapper.detect_face, - "yolov8n": Yolov8faceWrapper.detect_face, + "yolov8n": YoloWrapper.detect_face, } detect_face_fn = backends.get(detector_backend) diff --git a/deepface/detectors/YoloWrapper.py b/deepface/detectors/YoloWrapper.py new file mode 100644 index 0000000..f5b871b --- /dev/null +++ b/deepface/detectors/YoloWrapper.py @@ -0,0 +1,61 @@ +from deepface.detectors import FaceDetector + +# Model's weights paths +PATH = "/.deepface/weights/yolov8n-face.pt" + +# Google Drive URL +WEIGHT_URL = "https://drive.google.com/uc?id=1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb" + +# Confidence thresholds for landmarks detection +# used in alignment_procedure function +LANDMARKS_CONFIDENCE_THRESHOLD = 0.5 + + +def build_model(): + """Build YOLO (yolov8n-face) model""" + import gdown + import os + + # Import the Ultralytics YOLO model + from ultralytics import YOLO + + from deepface.commons.functions import get_deepface_home + weight_path = f"{get_deepface_home()}{PATH}" + + # Download the model's weights if they don't exist + if not os.path.isfile(weight_path): + gdown.download(WEIGHT_URL, weight_path, quiet=False) + print(f"Downloaded YOLO model {os.path.basename(weight_path)}") + + # Return face_detector + return YOLO(weight_path) + + +def detect_face(face_detector, img, align=False): + resp = [] + + # Detect faces + results = face_detector.predict( + img, verbose=False, show=False, conf=0.25)[0] + + # For each face, extract the bounding box, the landmarks and confidence + for result in results: + # Extract the bounding box and the confidence + x, y, w, h = result.boxes.xywh.tolist()[0] + confidence = result.boxes.conf.tolist()[0] + + x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) + detected_face = img[y: y + h, x: x + w].copy() + + if align: + # Extract landmarks + left_eye, right_eye, _, _, _ = result.keypoints.tolist() + # Check the landmarks confidence before alignment + if left_eye[2] > LANDMARKS_CONFIDENCE_THRESHOLD and right_eye[2] > LANDMARKS_CONFIDENCE_THRESHOLD: + detected_face = FaceDetector.alignment_procedure( + detected_face, left_eye[:2], right_eye[:2] + ) + + resp.append((detected_face, [x, y, w, h], confidence)) + + return resp diff --git a/deepface/detectors/Yolov8faceWrapper.py b/deepface/detectors/Yolov8faceWrapper.py deleted file mode 100644 index 0e9142d..0000000 --- a/deepface/detectors/Yolov8faceWrapper.py +++ /dev/null @@ -1,71 +0,0 @@ -from deepface.detectors import FaceDetector - -# Models names and paths -PATHS = { - "yolov8n": "/.deepface/weights/yolov8n-face.pt", -} - -# Google Drive base URL -BASE_URL = "https://drive.google.com/uc?id=" - -# Models' Google Drive IDs -IDS = { - "yolov8n": "1qcr9DbgsX3ryrz2uU8w4Xm3cOrRywXqb", -} - - -def build_model(model: str): - """Function factory for YOLO models""" - from deepface.commons.functions import get_deepface_home - - # Get model's weights path and Google Drive URL - func_weights_path = f"{get_deepface_home()}{PATHS[model]}" - func_url = f"{BASE_URL}{IDS[model]}" - - # Define function to build the model - def _build_model(weights_path: str = func_weights_path, url: str = func_url): - import gdown - import os - - # Import the Ultralytics YOLO model - from ultralytics import YOLO - - # Download the model's weights if they don't exist - if not os.path.isfile(weights_path): - gdown.download(url, weights_path, quiet=False) - print(f"Downloaded YOLO model {os.path.basename(PATHS[model])}") - - # Return face_detector - return YOLO(weights_path) - - return _build_model - - -def detect_face(face_detector, img, align=False): - resp = [] - - # Detect faces - results = face_detector.predict( - img, verbose=False, show=False, conf=0.25)[0] - - # For each face, extract the bounding box, the landmarks and confidence - for result in results: - # Extract the bounding box and the confidence - x, y, w, h = result.boxes.xywh.tolist()[0] - confidence = result.boxes.conf.tolist()[0] - - x, y, w, h = int(x - w / 2), int(y - h / 2), int(w), int(h) - detected_face = img[y: y + h, x: x + w].copy() - - if align: - # Extract landmarks - left_eye, right_eye, _, _, _ = result.keypoints.tolist() - # Check the landmarks confidence before alignment - if left_eye[2] > 0.5 and right_eye[2] > 0.5: - detected_face = FaceDetector.alignment_procedure( - detected_face, left_eye[:2], right_eye[:2] - ) - - resp.append((detected_face, [x, y, w, h], confidence)) - - return resp diff --git a/requirements_additional.txt b/requirements_additional.txt index ee752b9..cae0790 100644 --- a/requirements_additional.txt +++ b/requirements_additional.txt @@ -1,3 +1,4 @@ opencv-contrib-python>=4.3.0.36 mediapipe>=0.8.7.3 -dlib>=19.20.0 \ No newline at end of file +dlib>=19.20.0 +ultralytics @ git+https://github.com/derronqi/yolov8-face.git@b623989575bdb78601b5ca717851e3d63ca9e01c \ No newline at end of file From 9792af410dd4ea08761596e7c60ace44c1d27e4b Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Fri, 23 Jun 2023 23:46:22 +0200 Subject: [PATCH 17/18] Fix linting error. --- deepface/detectors/YoloWrapper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deepface/detectors/YoloWrapper.py b/deepface/detectors/YoloWrapper.py index f5b871b..fd38a9b 100644 --- a/deepface/detectors/YoloWrapper.py +++ b/deepface/detectors/YoloWrapper.py @@ -51,7 +51,8 @@ def detect_face(face_detector, img, align=False): # Extract landmarks left_eye, right_eye, _, _, _ = result.keypoints.tolist() # Check the landmarks confidence before alignment - if left_eye[2] > LANDMARKS_CONFIDENCE_THRESHOLD and right_eye[2] > LANDMARKS_CONFIDENCE_THRESHOLD: + if (left_eye[2] > LANDMARKS_CONFIDENCE_THRESHOLD and + right_eye[2] > LANDMARKS_CONFIDENCE_THRESHOLD): detected_face = FaceDetector.alignment_procedure( detected_face, left_eye[:2], right_eye[:2] ) From 64f93d1fcccf20ca25c32e9a5fe4cdc2672f23b7 Mon Sep 17 00:00:00 2001 From: Vincent STRAGIER Date: Sat, 24 Jun 2023 00:05:48 +0200 Subject: [PATCH 18/18] Change `yolov8n` to `yolov8`. --- README.md | 2 +- deepface/DeepFace.py | 10 +++++----- deepface/detectors/FaceDetector.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 33100b0..106bdc0 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ backends = [ 'mtcnn', 'retinaface', 'mediapipe', - 'yolov8n', + 'yolov8', ] #face verification diff --git a/deepface/DeepFace.py b/deepface/DeepFace.py index d7cd1c6..38aa545 100644 --- a/deepface/DeepFace.py +++ b/deepface/DeepFace.py @@ -116,7 +116,7 @@ def verify( This might be convenient for low resolution images. detector_backend (string): set face detector backend to opencv, retinaface, mtcnn, ssd, - dlib, mediapipe or yolov8n. + dlib, mediapipe or yolov8. align (boolean): alignment according to the eye positions. @@ -251,7 +251,7 @@ def analyze( resolution images. detector_backend (string): set face detector backend to opencv, retinaface, mtcnn, ssd, - dlib, mediapipe or yolov8n. + dlib, mediapipe or yolov8. align (boolean): alignment according to the eye positions. @@ -429,7 +429,7 @@ def find( resolution images. detector_backend (string): set face detector backend to opencv, retinaface, mtcnn, ssd, - dlib, mediapipe or yolov8n. + dlib, mediapipe or yolov8. align (boolean): alignment according to the eye positions. @@ -641,7 +641,7 @@ def represent( This might be convenient for low resolution images. detector_backend (string): set face detector backend to opencv, retinaface, mtcnn, ssd, - dlib, mediapipe or yolov8n. + dlib, mediapipe or yolov8. align (boolean): alignment according to the eye positions. @@ -726,7 +726,7 @@ def stream( model_name (string): VGG-Face, Facenet, Facenet512, OpenFace, DeepFace, DeepID, Dlib, ArcFace, SFace - detector_backend (string): opencv, retinaface, mtcnn, ssd, dlib, mediapipe or yolov8n. + detector_backend (string): opencv, retinaface, mtcnn, ssd, dlib, mediapipe or yolov8. distance_metric (string): cosine, euclidean, euclidean_l2 diff --git a/deepface/detectors/FaceDetector.py b/deepface/detectors/FaceDetector.py index f43841b..6e7f258 100644 --- a/deepface/detectors/FaceDetector.py +++ b/deepface/detectors/FaceDetector.py @@ -23,7 +23,7 @@ def build_model(detector_backend): "mtcnn": MtcnnWrapper.build_model, "retinaface": RetinaFaceWrapper.build_model, "mediapipe": MediapipeWrapper.build_model, - "yolov8n": YoloWrapper.build_model, + "yolov8": YoloWrapper.build_model, } if not "face_detector_obj" in globals(): @@ -66,7 +66,7 @@ def detect_faces(face_detector, detector_backend, img, align=True): "mtcnn": MtcnnWrapper.detect_face, "retinaface": RetinaFaceWrapper.detect_face, "mediapipe": MediapipeWrapper.detect_face, - "yolov8n": YoloWrapper.detect_face, + "yolov8": YoloWrapper.detect_face, } detect_face_fn = backends.get(detector_backend)