face_detection / tests /test_detector.py
Boopathy Sivakumar
Initial commit without binary files
9e89884
Raw
History Blame Contribute Delete
3.04 kB
"""Tests for the face + expression detector.
The detector depends only on numpy and OpenCV's cascade interface. We inject
fake cascades so the tests are deterministic and need no real camera/images.
"""
import numpy as np
import pytest
from face_detection.detector import Face, FaceDetector
class FakeCascade:
"""Mimics cv2.CascadeClassifier: returns canned detections per call."""
def __init__(self, *detections):
# Each call to detectMultiScale pops the next canned result.
self._results = list(detections)
self.calls = []
def detectMultiScale(self, image, **kwargs): # noqa: N802 (cv2 naming)
self.calls.append(image.shape)
result = self._results.pop(0) if self._results else []
return np.array(result, dtype=int) if len(result) else np.empty((0, 4), int)
def gray_frame(h=200, w=200):
return np.zeros((h, w), dtype=np.uint8)
def color_frame(h=200, w=200):
return np.zeros((h, w, 3), dtype=np.uint8)
def test_no_face_returns_empty_list():
det = FaceDetector(face_cascade=FakeCascade([]), smile_cascade=FakeCascade())
assert det.detect(gray_frame()) == []
def test_single_face_neutral_when_no_smile():
faces = FakeCascade([(10, 20, 50, 50)])
smiles = FakeCascade([]) # no smile inside the face ROI
det = FaceDetector(face_cascade=faces, smile_cascade=smiles)
result = det.detect(gray_frame())
assert len(result) == 1
assert isinstance(result[0], Face)
assert result[0].bbox == (10, 20, 50, 50)
assert result[0].expression == "neutral"
def test_single_face_happy_when_smile_detected():
faces = FakeCascade([(10, 20, 50, 50)])
smiles = FakeCascade([(5, 30, 20, 10)]) # a smile within the ROI
det = FaceDetector(face_cascade=faces, smile_cascade=smiles)
result = det.detect(gray_frame())
assert result[0].expression == "happy"
def test_multiple_faces_detected():
faces = FakeCascade([(0, 0, 40, 40), (100, 100, 30, 30)])
# One smile lookup per detected face; first happy, second neutral.
smiles = FakeCascade([(1, 1, 5, 5)], [])
det = FaceDetector(face_cascade=faces, smile_cascade=smiles)
result = det.detect(gray_frame())
assert len(result) == 2
assert result[0].expression == "happy"
assert result[1].expression == "neutral"
def test_color_frame_is_converted_to_gray_before_detection():
faces = FakeCascade([(10, 20, 50, 50)])
det = FaceDetector(face_cascade=faces, smile_cascade=FakeCascade([]))
det.detect(color_frame())
# The image handed to the cascade must be 2D (grayscale).
assert len(faces.calls[0]) == 2
def test_face_center_helper():
face = Face(bbox=(10, 20, 40, 60), expression="neutral")
assert face.center == (30, 50) # (10 + 40/2, 20 + 60/2)
def test_largest_face_picks_biggest_bbox():
small = Face(bbox=(0, 0, 10, 10), expression="neutral")
big = Face(bbox=(0, 0, 80, 80), expression="happy")
assert FaceDetector.largest([small, big]) is big
assert FaceDetector.largest([]) is None