KnowingFly/depression-detection-api
0
1import cv22import numpy as np3from typing import Optional, Tuple, List4 5 6class FaceDetector:7 """Face detection utility with multiple detection strategies."""8 9 def __init__(self):10 """Initialize the face detector with multiple Haar Cascade classifiers."""11 # Primary detector - frontal face12 self.face_cascade = cv2.CascadeClassifier(13 cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'14 )15 # Alternative detector - frontal face alt16 self.face_cascade_alt = cv2.CascadeClassifier(17 cv2.data.haarcascades + 'haarcascade_frontalface_alt.xml'18 )19 # Another alternative - frontal face alt220 self.face_cascade_alt2 = cv2.CascadeClassifier(21 cv2.data.haarcascades + 'haarcascade_frontalface_alt2.xml'22 )23 24 def _detect_with_cascade(25 self, 26 gray: np.ndarray, 27 cascade: cv2.CascadeClassifier,28 scale_factor: float = 1.1,29 min_neighbors: int = 3,30 min_size: Tuple[int, int] = (20, 20)31 ) -> List:32 """Detect faces using a specific cascade classifier."""33 try:34 faces = cascade.detectMultiScale(35 gray,36 scaleFactor=scale_factor,37 minNeighbors=min_neighbors,38 minSize=min_size,39 flags=cv2.CASCADE_SCALE_IMAGE40 )41 return list(faces) if len(faces) > 0 else []42 except Exception:43 return []44 45 def detect_and_crop_face(46 self, 47 image: np.ndarray, 48 target_size: Tuple[int, int] = (224, 224),49 padding: float = 0.250 ) -> Optional[np.ndarray]:51 """52 Detect face in image and crop it with padding.53 Uses multiple detection strategies for robustness.54 55 Args:56 image: Input image as numpy array (BGR format from OpenCV)57 target_size: Target size for the cropped face (width, height)58 padding: Padding ratio to add around the detected face59 60 Returns:61 Cropped and resized face image, or None if no face detected62 """63 if image is None or image.size == 0:64 print("Warning: Empty or invalid image received")65 return None66 67 # Convert to grayscale68 try:69 if len(image.shape) == 2:70 gray = image71 else:72 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)73 except Exception as e:74 print(f"Warning: Failed to convert to grayscale: {e}")75 return None76 77 # Enhance contrast for better detection78 gray = cv2.equalizeHist(gray)79 80 faces = []81 82 # Strategy 1: Primary cascade with standard parameters83 faces = self._detect_with_cascade(gray, self.face_cascade, 1.1, 5, (30, 30))84 85 # Strategy 2: Primary cascade with more lenient parameters86 if len(faces) == 0:87 faces = self._detect_with_cascade(gray, self.face_cascade, 1.05, 3, (20, 20))88 89 # Strategy 3: Alt cascade90 if len(faces) == 0:91 faces = self._detect_with_cascade(gray, self.face_cascade_alt, 1.1, 3, (20, 20))92 93 # Strategy 4: Alt2 cascade94 if len(faces) == 0:95 faces = self._detect_with_cascade(gray, self.face_cascade_alt2, 1.1, 3, (20, 20))96 97 # Strategy 5: Very lenient detection98 if len(faces) == 0:99 faces = self._detect_with_cascade(gray, self.face_cascade, 1.02, 2, (15, 15))100 101 # Strategy 6: Try with different image sizes102 if len(faces) == 0:103 # Try with scaled up image104 scale = 1.5105 scaled = cv2.resize(gray, None, fx=scale, fy=scale, interpolation=cv2.INTER_LINEAR)106 faces = self._detect_with_cascade(scaled, self.face_cascade, 1.1, 3, (30, 30))107 if len(faces) > 0:108 # Scale coordinates back109 faces = [(int(x/scale), int(y/scale), int(w/scale), int(h/scale)) 110 for (x, y, w, h) in faces]111 112 # Strategy 7: If still no face, use the entire image as the face region113 # This is a fallback for cases where face detection fails but user insists image has a face114 if len(faces) == 0:115 print("Warning: No face detected with any strategy. Using center crop as fallback.")116 h, w = image.shape[:2]117 # Use center 70% of the image118 margin_w = int(w * 0.15)119 margin_h = int(h * 0.15)120 face_crop = image[margin_h:h-margin_h, margin_w:w-margin_w]121 122 if face_crop.size > 0:123 face_resized = cv2.resize(face_crop, target_size, interpolation=cv2.INTER_AREA)124 if len(face_resized.shape) == 3:125 face_rgb = cv2.cvtColor(face_resized, cv2.COLOR_BGR2RGB)126 else:127 face_rgb = cv2.cvtColor(face_resized, cv2.COLOR_GRAY2RGB)128 return face_rgb129 return None130 131 # Get the largest face132 x, y, w, h = max(faces, key=lambda f: f[2] * f[3])133 134 # Add padding135 pad_w = int(w * padding)136 pad_h = int(h * padding)137 138 img_h, img_w = image.shape[:2]139 x1 = max(0, x - pad_w)140 y1 = max(0, y - pad_h)141 x2 = min(img_w, x + w + pad_w)142 y2 = min(img_h, y + h + pad_h)143 144 face_crop = image[y1:y2, x1:x2]145 146 if face_crop.size == 0:147 return None148 149 face_resized = cv2.resize(face_crop, target_size, interpolation=cv2.INTER_AREA)150 151 # Convert to RGB152 if len(face_resized.shape) == 3:153 face_rgb = cv2.cvtColor(face_resized, cv2.COLOR_BGR2RGB)154 else:155 face_rgb = cv2.cvtColor(face_resized, cv2.COLOR_GRAY2RGB)156 157 return face_rgb158 159 def detect_faces_count(self, image: np.ndarray) -> int:160 """161 Count number of faces detected in image.162 163 Args:164 image: Input image as numpy array165 166 Returns:167 Number of faces detected168 """169 try:170 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)171 gray = cv2.equalizeHist(gray)172 173 faces = self._detect_with_cascade(gray, self.face_cascade, 1.1, 3, (20, 20))174 return len(faces)175 except Exception:176 return 0177 178 179face_detector = FaceDetector()180 