File size: 2,946 Bytes
892fa81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Unit tests for cores.face — box conversions, embedding distance, matching."""

from __future__ import annotations

import numpy as np

from cores.face import (
    xywh_to_xyxy, xyxy_to_xywh, xywh_to_face_recognition_tuple,
    cosine_similarity, euclidean_distance, best_match,
)


class TestBoxConversions:
    def test_xywh_to_xyxy(self):
        assert xywh_to_xyxy(10, 20, 100, 50) == (10, 20, 110, 70)

    def test_xyxy_to_xywh(self):
        assert xyxy_to_xywh(10, 20, 110, 70) == (10, 20, 100, 50)

    def test_face_recognition_tuple(self):
        # face_recognition uses (top, right, bottom, left)
        assert xywh_to_face_recognition_tuple(10, 20, 100, 50) == (20, 110, 70, 10)


class TestEmbeddingDistance:
    def test_cosine_similarity_identical(self):
        v = np.array([1.0, 2.0, 3.0])
        assert cosine_similarity(v, v) == pytest.approx(1.0) if (pytest := __import__("pytest")) else True

    def test_cosine_similarity_orthogonal(self):
        a = np.array([1.0, 0.0])
        b = np.array([0.0, 1.0])
        assert cosine_similarity(a, b) == 0.0

    def test_cosine_similarity_zero_vector(self):
        a = np.zeros(3)
        b = np.array([1.0, 2.0, 3.0])
        assert cosine_similarity(a, b) == 0.0

    def test_euclidean_distance_identical(self):
        v = np.array([1.0, 2.0, 3.0])
        assert euclidean_distance(v, v) == 0.0

    def test_euclidean_distance_known(self):
        a = np.array([0.0, 0.0])
        b = np.array([3.0, 4.0])
        assert euclidean_distance(a, b) == 5.0


class TestBestMatch:
    def test_empty_gallery_returns_none(self):
        name, score, all_scores = best_match(np.zeros(128), {})
        assert name is None
        assert all_scores == {}

    def test_finds_best_match_cosine(self):
        query = np.array([1.0, 0.0, 0.0])
        gallery = {
            "alice": [np.array([0.95, 0.05, 0.0])],  # close to query
            "bob": [np.array([0.0, 1.0, 0.0])],       # orthogonal
        }
        name, score, all_scores = best_match(query, gallery, metric="cosine")
        assert name == "alice"
        assert score > 0.9
        assert "alice" in all_scores
        assert "bob" in all_scores
        assert all_scores["alice"] > all_scores["bob"]

    def test_finds_best_match_euclidean(self):
        query = np.array([0.0, 0.0, 0.0])
        gallery = {
            "near": [np.array([1.0, 0.0, 0.0])],   # distance 1
            "far": [np.array([5.0, 5.0, 5.0])],     # distance ~8.66
        }
        name, score, all_scores = best_match(query, gallery, metric="euclidean")
        assert name == "near"
        assert score == 1.0
        assert all_scores["near"] < all_scores["far"]

    def test_handles_empty_person_embeddings(self):
        query = np.array([1.0, 0.0])
        gallery = {"empty_person": []}
        name, score, all_scores = best_match(query, gallery)
        assert name is None
        assert all_scores == {}