From 5e55fafdb56b43185befffb23f8b7bad5335b7d0 Mon Sep 17 00:00:00 2001 From: "Anthr@X" Date: Sat, 8 Jul 2023 23:17:50 +1000 Subject: [PATCH 1/5] Add Yunet detector --- deepface/detectors/FaceDetector.py | 3 ++ deepface/detectors/YunetWrapper.py | 64 ++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 deepface/detectors/YunetWrapper.py diff --git a/deepface/detectors/FaceDetector.py b/deepface/detectors/FaceDetector.py index 6e7f258..522592d 100644 --- a/deepface/detectors/FaceDetector.py +++ b/deepface/detectors/FaceDetector.py @@ -10,6 +10,7 @@ from deepface.detectors import ( RetinaFaceWrapper, MediapipeWrapper, YoloWrapper, + YunetWrapper, ) @@ -24,6 +25,7 @@ def build_model(detector_backend): "retinaface": RetinaFaceWrapper.build_model, "mediapipe": MediapipeWrapper.build_model, "yolov8": YoloWrapper.build_model, + "yunet": YunetWrapper.build_model, } if not "face_detector_obj" in globals(): @@ -67,6 +69,7 @@ def detect_faces(face_detector, detector_backend, img, align=True): "retinaface": RetinaFaceWrapper.detect_face, "mediapipe": MediapipeWrapper.detect_face, "yolov8": YoloWrapper.detect_face, + "yunet": YunetWrapper.detect_face, } detect_face_fn = backends.get(detector_backend) diff --git a/deepface/detectors/YunetWrapper.py b/deepface/detectors/YunetWrapper.py new file mode 100644 index 0000000..9d184df --- /dev/null +++ b/deepface/detectors/YunetWrapper.py @@ -0,0 +1,64 @@ +import numpy as np +import cv2 +import os +import gdown +from deepface.detectors import FaceDetector +from deepface.commons import functions + + +def build_model(): + url = "https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx" + file_name = "face_detection_yunet_2023mar.onnx" + home = functions.get_deepface_home() + if os.path.isfile(home + f"/.deepface/weights/{file_name}") is False: + print("yunet_n_dynamic.onnx will be downloaded...") + output = home + f"/.deepface/weights/{file_name}" + gdown.download(url, output, quiet=False) + face_detector = cv2.FaceDetectorYN_create( + home + f"/.deepface/weights/{file_name}", "", (0, 0) + ) + return face_detector + + +def detect_face(detector, image, align=True, score_threshold=0.8): + # FaceDetector.detect_faces does not support score_threshold parameter. + # We can set it via environment variable. + score_threshold = os.environ.get("yunet_score_threshold", score_threshold) + resp = [] + detected_face = None + img_region = [0, 0, image.shape[1], image.shape[0]] + faces = [] + height, width = image.shape[0], image.shape[1] + # resize image if it is too large (Yunet fails to detect faces on large input sometimes) + # I picked 640 as a threshold because it is the default value of max_size in Yunet. + if height > 640 or width > 640: + r = 640.0 / max(height, width) + image = cv2.resize(image, (int(width * r), int(height * r))) + height, width = image.shape[0], image.shape[1] + detector.setInputSize((width, height)) + detector.setScoreThreshold(score_threshold) + _, faces = detector.detect(image) + for face in faces: + """ + The detection output faces is a two-dimension array of type CV_32F, + whose rows are the detected face instances, columns are the location of a face and 5 facial landmarks. + The format of each row is as follows: + x1, y1, w, h, x_re, y_re, x_le, y_le, x_nt, y_nt, x_rcm, y_rcm, x_lcm, y_lcm, + where x1, y1, w, h are the top-left coordinates, width and height of the face bounding box, + {x, y}_{re, le, nt, rcm, lcm} stands for the coordinates of right eye, left eye, nose tip, the right corner and left corner of the mouth respectively. + """ + (x, y, w, h, x_re, y_re, x_le, y_le) = list(map(int, face[:8])) + confidence = face[-1] + confidence = "{:.2f}".format(confidence) + detected_face = image[int(y) : int(y + h), int(x) : int(x + w)] + img_region = [x, y, w, h] + if align: + detected_face = yunet_align_face(detected_face, x_re, y_re, x_le, y_le) + resp.append((detected_face, img_region, confidence)) + return resp + + +# x_re, y_re, x_le, y_le stands for the coordinates of right eye, left eye +def yunet_align_face(img, x_re, y_re, x_le, y_le): + img = FaceDetector.alignment_procedure(img, (x_le, y_le), (x_re, y_re)) + return img From a90bf8e1c017e501c9b4b03bfda84eb85a388331 Mon Sep 17 00:00:00 2001 From: "Anthr@X" Date: Sat, 8 Jul 2023 23:29:28 +1000 Subject: [PATCH 2/5] Fix hard coded file name in message --- deepface/detectors/YunetWrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepface/detectors/YunetWrapper.py b/deepface/detectors/YunetWrapper.py index 9d184df..a9fb02e 100644 --- a/deepface/detectors/YunetWrapper.py +++ b/deepface/detectors/YunetWrapper.py @@ -11,7 +11,7 @@ def build_model(): file_name = "face_detection_yunet_2023mar.onnx" home = functions.get_deepface_home() if os.path.isfile(home + f"/.deepface/weights/{file_name}") is False: - print("yunet_n_dynamic.onnx will be downloaded...") + print(f"{file_name} will be downloaded...") output = home + f"/.deepface/weights/{file_name}" gdown.download(url, output, quiet=False) face_detector = cv2.FaceDetectorYN_create( From 34d1f1250a65ae587729fbf0fbac00e33843122b Mon Sep 17 00:00:00 2001 From: "Anthr@X" Date: Sun, 9 Jul 2023 00:23:31 +1000 Subject: [PATCH 3/5] return empty list if detected no face. --- deepface/detectors/YunetWrapper.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deepface/detectors/YunetWrapper.py b/deepface/detectors/YunetWrapper.py index a9fb02e..e9b7905 100644 --- a/deepface/detectors/YunetWrapper.py +++ b/deepface/detectors/YunetWrapper.py @@ -1,4 +1,3 @@ -import numpy as np import cv2 import os import gdown @@ -20,7 +19,7 @@ def build_model(): return face_detector -def detect_face(detector, image, align=True, score_threshold=0.8): +def detect_face(detector, image, align=True, score_threshold=0.9): # FaceDetector.detect_faces does not support score_threshold parameter. # We can set it via environment variable. score_threshold = os.environ.get("yunet_score_threshold", score_threshold) @@ -38,6 +37,8 @@ def detect_face(detector, image, align=True, score_threshold=0.8): detector.setInputSize((width, height)) detector.setScoreThreshold(score_threshold) _, faces = detector.detect(image) + if faces is None: + return resp for face in faces: """ The detection output faces is a two-dimension array of type CV_32F, From b52d1e8015ada4e00806ef69b267f8600b2c0dde Mon Sep 17 00:00:00 2001 From: "Anthr@X" Date: Sun, 9 Jul 2023 01:05:17 +1000 Subject: [PATCH 4/5] restore original size and move coordinates accordingly --- deepface/detectors/YunetWrapper.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/deepface/detectors/YunetWrapper.py b/deepface/detectors/YunetWrapper.py index e9b7905..dc7b1b2 100644 --- a/deepface/detectors/YunetWrapper.py +++ b/deepface/detectors/YunetWrapper.py @@ -30,10 +30,13 @@ def detect_face(detector, image, align=True, score_threshold=0.9): height, width = image.shape[0], image.shape[1] # resize image if it is too large (Yunet fails to detect faces on large input sometimes) # I picked 640 as a threshold because it is the default value of max_size in Yunet. + resized = False if height > 640 or width > 640: r = 640.0 / max(height, width) + original_image = image.copy() image = cv2.resize(image, (int(width * r), int(height * r))) height, width = image.shape[0], image.shape[1] + resized = True detector.setInputSize((width, height)) detector.setScoreThreshold(score_threshold) _, faces = detector.detect(image) @@ -49,6 +52,15 @@ def detect_face(detector, image, align=True, score_threshold=0.9): {x, y}_{re, le, nt, rcm, lcm} stands for the coordinates of right eye, left eye, nose tip, the right corner and left corner of the mouth respectively. """ (x, y, w, h, x_re, y_re, x_le, y_le) = list(map(int, face[:8])) + if resized: + image = original_image + x, y, w, h = int(x / r), int(y / r), int(w / r), int(h / r) + x_re, y_re, x_le, y_le = ( + int(x_re / r), + int(y_re / r), + int(x_le / r), + int(y_le / r), + ) confidence = face[-1] confidence = "{:.2f}".format(confidence) detected_face = image[int(y) : int(y + h), int(x) : int(x + w)] From 4b253e115c5c47e18da18d62764cdee3ff45b758 Mon Sep 17 00:00:00 2001 From: "Anthr@X" Date: Sun, 9 Jul 2023 01:11:35 +1000 Subject: [PATCH 5/5] add yunet in README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 106bdc0..2bbeeda 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/), [`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. +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/), [`YOLOv8 Face`](https://github.com/derronqi/yolov8-face) and [`YuNet`](https://github.com/ShiqiYu/libfacedetection) detectors are wrapped in deepface.

@@ -209,6 +209,7 @@ backends = [ 'retinaface', 'mediapipe', 'yolov8', + 'yunet', ] #face verification