Skip to content

Commit e05c31e

Browse files
authored
Replace Haar Cascade by YuNet for face detection (#546)
* Replace Haar Cascade by YuNet * Cast numbers to float
1 parent fd3eb6c commit e05c31e

File tree

3 files changed

+41
-33331
lines changed

3 files changed

+41
-33331
lines changed

gramps_webapi/api/image.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919

2020
"""Image utilities."""
2121

22+
from __future__ import annotations
2223

2324
import io
2425
import os
2526
import shutil
2627
import tempfile
2728
from pathlib import Path
28-
from typing import BinaryIO, Callable, List, Tuple
29+
from typing import BinaryIO, Callable
2930

3031
import ffmpeg
3132
from pdf2image import convert_from_path
@@ -218,26 +219,49 @@ def __init__(self, path: FilenameOrPath, mime_type: str) -> None:
218219
super().__init__(stream=stream, mime_type=mime_type)
219220

220221

221-
def detect_faces(stream: BinaryIO) -> List[Tuple[float, float, float, float]]:
222-
"""Detect faces in an image (stream)."""
222+
def detect_faces(stream: BinaryIO) -> list[tuple[float, float, float, float]]:
223+
"""Detect faces in an image (stream) using YuNet."""
224+
# Read the image from the input stream
223225
import cv2
224226
import numpy as np
225227

226228
file_bytes = np.asarray(bytearray(stream.read()), dtype=np.uint8)
227229
cv_image = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
228-
cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)
229-
haarcascade_path = resource_filename(
230-
"gramps_webapi", "data/haarcascade_frontalface_default.xml"
230+
231+
# Load the YuNet model
232+
model_path = resource_filename(
233+
"gramps_webapi", "data/face_detection_yunet_2023mar.onnx"
234+
)
235+
face_detector = cv2.FaceDetectorYN.create(
236+
model_path, "", (320, 320), score_threshold=0.5
231237
)
232-
cascade = cv2.CascadeClassifier(haarcascade_path)
233-
faces = cascade.detectMultiScale(cv_image, 1.1, 4)
234-
height, width = cv_image.shape
235-
return [
236-
(
237-
100 * x / width,
238-
100 * y / height,
239-
100 * (x + w) / width,
240-
100 * (y + h) / height,
238+
239+
# Set input image size for YuNet
240+
height, width, _ = cv_image.shape
241+
face_detector.setInputSize((width, height))
242+
243+
# Detect faces
244+
faces = face_detector.detect(cv_image)
245+
246+
# Check if any faces are detected
247+
if faces[1] is None:
248+
return []
249+
250+
# Extract and normalize face bounding boxes
251+
detected_faces = []
252+
for face in faces[1]:
253+
x, y, w, h = face[:4]
254+
x = float(x)
255+
y = float(y)
256+
w = float(w)
257+
h = float(h)
258+
detected_faces.append(
259+
(
260+
100 * x / width,
261+
100 * y / height,
262+
100 * (x + w) / width,
263+
100 * (y + h) / height,
264+
)
241265
)
242-
for (x, y, w, h) in faces
243-
]
266+
267+
return detected_faces
227 KB
Binary file not shown.

0 commit comments

Comments
 (0)