File size: 4,957 Bytes
7e25f7a | 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | """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) == []
|