File size: 1,918 Bytes
887f5f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import cv2
import numpy as np

class FaceDetector:
    """
    Detects faces in images/frames using OpenCV's Haar Cascade classifier.
    """
    def __init__(self):
        # Path to Haar cascade file included in opencv-python
        cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
        self.face_cascade = cv2.CascadeClassifier(cascade_path)
        if self.face_cascade.empty():
            raise RuntimeError("Failed to load Haar cascade for face detection.")

    def detect_faces(self, frame_rgb: np.ndarray) -> list[dict]:
        """
        Detects faces in an RGB image.
        Returns a list of dicts: {'box': [x, y, w, h], 'face_crop': np.ndarray}
        """
        # OpenCV Haar cascade expects grayscale and BGR layout for speed
        # But we pass RGB, so convert to grayscale directly
        gray = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2GRAY)
        
        # Detect faces
        faces = self.face_cascade.detectMultiScale(
            gray,
            scaleFactor=1.1,
            minNeighbors=8,
            minSize=(60, 60),
            flags=cv2.CASCADE_SCALE_IMAGE
        )

        detected = []
        for (x, y, w, h) in faces:
            # Add small padding to crop to capture full facial features
            pad_h = int(h * 0.1)
            pad_w = int(w * 0.1)
            
            y_start = max(0, y - pad_h)
            y_end = min(frame_rgb.shape[0], y + h + pad_h)
            x_start = max(0, x - pad_w)
            x_end = min(frame_rgb.shape[1], x + w + pad_w)
            
            face_crop = frame_rgb[y_start:y_end, x_start:x_end]
            
            detected.append({
                "box": [int(x), int(y), int(w), int(h)],
                "padded_box": [int(x_start), int(y_start), int(x_end - x_start), int(y_end - y_start)],
                "face_crop": face_crop
            })
            
        return detected