File size: 2,136 Bytes
8a4d3a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Face verification utilities.

This module provides functions to compare face embeddings and decide
whether two faces belong to the same person.  It relies on
``extract_embeddings.extract_embedding`` to obtain the embeddings.
"""

from typing import Tuple, Optional

import numpy as np
from PIL import Image

from .extract_embeddings import extract_embedding


def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
    """Compute the cosine similarity between two vectors.

    Parameters
    ----------
    a, b: np.ndarray
        1D vectors of the same length.

    Returns
    -------
    float
        The cosine similarity ranging from -1 (opposite) to 1 (identical).
    """
    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))


def verify_images(img1: Image.Image, img2: Image.Image, threshold: float = 0.8, device: str = "cpu") -> Tuple[Optional[float], bool, str]:
    """Verify whether two images depict the same person.

    This function detects faces in each image, extracts embeddings and
    computes the cosine similarity.  A threshold decides whether the
    similarity represents the same identity.

    Parameters
    ----------
    img1, img2: PIL.Image.Image
        The two images to compare.
    threshold: float, optional
        Similarity threshold above which the faces are considered the
        same person.  Defaults to 0.8.
    device: str, optional
        Device to run the embedding extraction on.  Defaults to ``"cpu"``.

    Returns
    -------
    Tuple[Optional[float], bool, str]
        A tuple of (similarity score, decision, message).  If no face is
        detected in either image, the similarity is ``None`` and the
        decision is ``False``.
    """
    emb1 = extract_embedding(img1, device=device)
    emb2 = extract_embedding(img2, device=device)
    if emb1 is None or emb2 is None:
        return None, False, "Face not detected in one or both images."
    sim = cosine_similarity(emb1, emb2)
    is_same = sim >= threshold
    return sim, is_same, "Same person" if is_same else "Different people"


__all__ = ["verify_images", "cosine_similarity"]