"""Unit tests for cores.face.analysis — quality, blur, pose, clustering.""" from __future__ import annotations import numpy as np import pytest import cv2 from cores.face import ( blur_score, is_blurry, face_size, face_size_label, estimate_pose_landmark, face_orientation, face_quality_score, select_best_face, cluster_faces, find_duplicate_faces, ) from cores.vision.geometry import BBox class TestBlurScore: def test_sharp_image_high_score(self): # Random noise is "sharp" img = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) assert blur_score(img) > 50 def test_uniform_image_low_score(self): img = np.full((100, 100, 3), 128, dtype=np.uint8) assert blur_score(img) < 1.0 def test_is_blurry_threshold(self): sharp = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) blurry = np.full((100, 100, 3), 128, dtype=np.uint8) assert is_blurry(sharp, threshold=10) is False assert is_blurry(blurry, threshold=10) is True class TestFaceSize: def test_face_size(self): assert face_size(BBox(0, 0, 100, 50)) == 5000 def test_face_size_label(self): assert face_size_label(BBox(0, 0, 40, 40)) == "small" # 1600 assert face_size_label(BBox(0, 0, 80, 80)) == "medium" # 6400 assert face_size_label(BBox(0, 0, 120, 120)) == "large" # 14400 class TestPoseEstimation: def test_no_landmarks_returns_unknown(self): yaw, pitch, roll, label = estimate_pose_landmark(None) assert label == "unknown" assert yaw == 0.0 def test_frontal_pose(self): # Symmetric landmarks → near-zero yaw landmarks = { "left_eye": (40, 50), "right_eye": (60, 50), "nose": (50, 60), } yaw, pitch, roll, label = estimate_pose_landmark(landmarks) assert abs(yaw) < 5.0 assert label == "frontal" def test_profile_pose(self): # Nose offset to one side → high yaw landmarks = { "left_eye": (40, 50), "right_eye": (60, 50), "nose": (75, 60), # shifted right } yaw, _, _, label = estimate_pose_landmark(landmarks) assert yaw > 15.0 assert label in ("profile", "extreme") class TestFaceQuality: def test_quality_score_in_range(self): img = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) bbox = BBox(0, 0, 100, 100) qs = face_quality_score(img, bbox) assert 0.0 <= qs <= 1.0 def test_uniform_image_low_quality(self): """A uniform (blurry) image should have lower quality than a sharp one.""" uniform = np.full((100, 100, 3), 128, dtype=np.uint8) sharp = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) bbox = BBox(0, 0, 100, 100) qs_uniform = face_quality_score(uniform, bbox) qs_sharp = face_quality_score(sharp, bbox) # Sharp should score higher than uniform assert qs_sharp > qs_uniform class TestSelectBestFace: def test_selects_frontal(self): # Two faces: one frontal, one profile idx = select_best_face( quality_scores=[0.5, 0.5], face_sizes=[10000, 10000], pose_labels=["frontal", "profile"], ) assert idx == 0 def test_selects_larger(self): idx = select_best_face( quality_scores=[0.5, 0.5], face_sizes=[5000, 15000], pose_labels=["frontal", "frontal"], ) assert idx == 1 class TestClusterFaces: def test_clusters_identical_embeddings(self): emb = np.random.randn(128).astype(np.float32) embeddings = [emb, emb, emb] clusters = cluster_faces(embeddings, threshold=0.9) assert len(clusters) == 1 assert clusters[0]["num_faces"] == 3 def test_separates_different_embeddings(self): emb1 = np.random.randn(128).astype(np.float32) emb2 = -emb1 # opposite direction embeddings = [emb1, emb2] clusters = cluster_faces(embeddings, threshold=0.9) assert len(clusters) == 2 def test_empty_embeddings(self): assert cluster_faces([]) == [] class TestDuplicateFaces: def test_overlapping_boxes_detected(self): boxes = [ {"x": 0, "y": 0, "w": 100, "h": 100}, {"x": 10, "y": 10, "w": 100, "h": 100}, # overlaps heavily ] dups = find_duplicate_faces(boxes, iou_threshold=0.5) assert 1 in dups def test_non_overlapping_not_duplicates(self): boxes = [ {"x": 0, "y": 0, "w": 50, "h": 50}, {"x": 200, "y": 200, "w": 50, "h": 50}, ] dups = find_duplicate_faces(boxes) assert dups == [] def test_single_face_no_duplicates(self): boxes = [{"x": 0, "y": 0, "w": 100, "h": 100}] assert find_duplicate_faces(boxes) == []