File size: 3,464 Bytes
9e1ec94
 
fae5be7
9e1ec94
2b7c418
9e1ec94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40a4317
 
9e1ec94
 
 
 
 
 
 
 
 
f430918
9e1ec94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fae5be7
9e1ec94
 
 
 
 
fae5be7
9e1ec94
 
 
 
 
 
 
 
 
 
 
fae5be7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e1ec94
 
 
 
 
22320ed
9e1ec94
 
 
 
2bc188c
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
import os
import json
import math

os.environ["DEEPFACE_HOME"] = "."

import pyzipper
import numpy as np
import gradio as gr
from annoy import AnnoyIndex
from deepface.commons import functions
from deepface.basemodels import Facenet512


# load the face model
model = Facenet512.loadModel()

input_shape_x, input_shape_y = functions.find_input_shape(model)

index = AnnoyIndex(512, "euclidean")
index.load(f"face.db")

ANNOY_INDEX = json.load(open(f"face.json"))

with pyzipper.AESZipFile('persons.zip') as zf:
    password = os.getenv("VISAGE_KEY","").encode('ascii')
    zf.setpassword(password)
    PERFORMER_DB = json.loads(zf.read('performers.json'))


def predict(image, threshold=20.0, results=3):
    image_array = np.array(image)

    img = functions.preprocess_face(
        img=image_array,
        target_size=(input_shape_x, input_shape_y),
        enforce_detection=True,
        detector_backend="retinaface",
        align=True,
    )

    img = functions.normalize_input(img, normalization="Facenet2018")
    face = model.predict(img)[0].tolist()
    ids, distances = index.get_nns_by_vector(
        face, 50, search_k=10000, include_distances=True
    )
    persons = {}
    for p, distance in zip(ids, distances):
        id = ANNOY_INDEX[p]
        if id in persons:
            persons[id]["hits"] += 1
            persons[id]["distance"] -= 0.5
            persons[id]["confidence"] = normalize_confidence_from_distance(persons[id]["distance"], threshold)
            continue

        persons[id] = {
            "id": id,
            "distance": round(distance, 2),
            "confidence": normalize_confidence_from_distance(distance, threshold),
            "hits": 1,
        }

        if id in PERFORMER_DB:
            persons[id].update(PERFORMER_DB.get(id))

    persons = sorted(persons.values(), key=lambda x: x["distance"])
    persons = [p for p in persons if p["distance"] < threshold]
    return persons[:results]


def normalize_confidence_from_distance(distance, threshold=20.0):
    """Normalize confidence to 0-100 scale"""
    confidence = face_distance_to_conf(distance, threshold)
    return int(((confidence - 0.0) / (1.0 - 0.0)) * (100.0 - 0.0) + 0.0)


def face_distance_to_conf(face_distance, face_match_threshold=20.0):
    """Using a face distance, calculate a similarity confidence value"""
    if face_distance > face_match_threshold:
        # The face is far away, so give it a low confidence
        range = (1.0 - face_match_threshold)
        linear_val = (1.0 - face_distance) / (range * 2.0)
        return linear_val
    else:
        # The face is close, so give it a high confidence
        range = face_match_threshold
        linear_val = 1.0 - (face_distance / (range * 2.0))
        # But adjust this value by a curve so that we don't get a linear
        # transition from close to far. We want it to be more confident
        # the closer it is.
        return linear_val + ((1.0 - linear_val) * math.pow((linear_val - 0.5) * 2, 0.2))


gr.Interface(
    fn=predict,
    inputs=[
        gr.components.Image(),
        gr.components.Slider(label="threshold",minimum=0.0, maximum=30.0, value=20.0),
        gr.components.Slider(label="results", minimum=0, maximum=50, value=3, step=1),
    ],
    outputs=gr.outputs.JSON(label=""),
    title="Who is in the photo?",
    description="Upload an image of a person and we'll tell you who it is.",
).launch(enable_queue=True, server_name="0.0.0.0")