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
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..dc7b1b2
--- /dev/null
+++ b/deepface/detectors/YunetWrapper.py
@@ -0,0 +1,77 @@
+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(f"{file_name} 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.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)
+ 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.
+ 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)
+ if faces is None:
+ return resp
+ 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]))
+ 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)]
+ 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