""" OpenCV Haar Cascade face detector. Uses cores.vision for all image operations — no duplicated logic. """ from __future__ import annotations import cv2 import numpy as np from config.settings import Settings, settings as _default_settings from cores.vision import to_gray, BBox from pipeline.feature_extraction import PipelineOutput from providers.base import BaseProvider, ProviderCapability class HaarDetector(BaseProvider): name = "haar" capability = ProviderCapability.DETECTION def __init__(self, settings: Settings | None = None) -> None: super().__init__(settings=settings or _default_settings) cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_default.xml" self._cascade = cv2.CascadeClassifier(cascade_path) if self._cascade.empty(): raise RuntimeError("Failed to load Haar cascade classifier.") def is_available(self) -> bool: return not self._cascade.empty() def _run(self, pipeline_output: PipelineOutput) -> tuple[dict, dict]: img: np.ndarray = pipeline_output.image s = self._settings gray = cv2.equalizeHist(to_gray(img)) rects = self._cascade.detectMultiScale( gray, scaleFactor=s.haar_scale_factor, minNeighbors=s.haar_min_neighbors, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE, ) boxes = [BBox(int(x), int(y), int(w), int(h)).to_dict() for (x, y, w, h) in rects] raw = { "rectangles": [[int(x), int(y), int(w), int(h)] for (x, y, w, h) in rects], "num_faces": len(rects), } normalized = { "boxes": boxes, "num_faces": len(boxes), "landmarks": None, "confidences": [1.0] * len(boxes), } return raw, normalized