Ismail Ashraq commited on
Commit
2a38579
1 Parent(s): 48e3aed
Files changed (5) hide show
  1. app.py +51 -0
  2. facenet.py +23 -0
  3. requirements.txt +6 -0
  4. style.css +170 -0
  5. templates/card.html +45 -0
app.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pinecone
2
+ from PIL import Image
3
+ import streamlit as st
4
+ from facenet import FacenetEmbedder
5
+ from jinja2 import Environment, FileSystemLoader
6
+
7
+
8
+ st.markdown("<h1 style='text-align: center;'>⚡️ Find Your Celebrity Match ⚡️</h1>", unsafe_allow_html=True)
9
+ st.markdown("<p style='text-align: center;'>Take a picture to find your match, learn how it works <a href='https://www.pinecone.io/learn/gif-search/'>here</a>.</p>", unsafe_allow_html=True)
10
+
11
+ def local_css(file_name):
12
+ st.markdown('<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">', unsafe_allow_html=True)
13
+ with open(file_name) as f:
14
+ st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
15
+
16
+ @st.experimental_singleton
17
+ def init_template():
18
+ local_css("style.css")
19
+ environment = Environment(loader=FileSystemLoader("templates/"))
20
+ template = environment.get_template("card.html")
21
+ return template
22
+
23
+ PINECONE_KEY = st.secrets["PINECONE_KEY"]
24
+
25
+ @st.experimental_singleton
26
+ def init_pinecone():
27
+ pinecone.init(api_key=PINECONE_KEY, environment="us-west1-gcp") # get a free api key from app.pinecone.io
28
+ return pinecone.GRPCIndex("tmdb-people-115k")
29
+
30
+
31
+ @st.experimental_singleton
32
+ def init_facenet():
33
+ facenet = FacenetEmbedder()
34
+ return facenet
35
+
36
+
37
+ template = init_template()
38
+ index = init_pinecone()
39
+ facenet = init_facenet()
40
+
41
+ col1, col2, col3 = st.columns([0.2, 2, 0.2])
42
+
43
+ with col2:
44
+ img_file_buffer = st.camera_input("")
45
+
46
+ if img_file_buffer is not None:
47
+ img = Image.open(img_file_buffer)
48
+ emb = facenet.encode([img])
49
+ result = index.query(emb[0], top_k=3, include_metadata=True)
50
+ cards = template.render(results=result["matches"])
51
+ st.markdown(f"<div class='container-md' align='center'><div class='row'>{cards}</div></div>", unsafe_allow_html=True)
facenet.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ from facenet_pytorch import MTCNN, InceptionResnetV1
4
+
5
+ class FacenetEmbedder:
6
+ def __init__(self):
7
+ self.device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
8
+ self.mtcnn = MTCNN(device=self.device)
9
+ self.resnet = InceptionResnetV1(pretrained='vggface2', device=self.device).eval()
10
+
11
+ def detect_face(self, batch):
12
+ faces = self.mtcnn.detect(batch)
13
+ return faces
14
+
15
+ def encode(self, batch):
16
+ face_batch = self.mtcnn(batch)
17
+ face_batch = [i for i in face_batch if i is not None]
18
+ aligned = torch.stack(face_batch)
19
+ if self.device.type == "cuda":
20
+ aligned = aligned.to(self.device)
21
+
22
+ embeddings = self.resnet(aligned).detach().cpu()
23
+ return embeddings.tolist()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ facenet-pytorch
2
+ pinecone-client[grpc]
3
+ torch
4
+ datasets
5
+ streamlit
6
+ jinja2
style.css ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url("https://fonts.googleapis.com/css?family=Arimo:400,700");
2
+
3
+ body {
4
+ height: 100%;
5
+ width: 100%;
6
+ background: #e9e9e9;
7
+ font-family: 'Arimo', Arial, sans-serif;
8
+ font-weight: 400;
9
+ font-size: 14px;
10
+ color: #010b26;
11
+ }
12
+
13
+ * {
14
+ -webkit-transition: 300ms;
15
+ transition: 300ms;
16
+ }
17
+
18
+ .intro {
19
+ text-align: center;
20
+ }
21
+
22
+ ul {
23
+ list-style-type: none;
24
+ }
25
+
26
+ h1,
27
+ h2,
28
+ h3,
29
+ h4,
30
+ h5,
31
+ p {
32
+ font-weight: 400;
33
+ }
34
+
35
+ a {
36
+ text-decoration: none;
37
+ color: inherit;
38
+ }
39
+
40
+ a:hover {
41
+ color: #6ABCEA;
42
+ }
43
+
44
+ .container {
45
+ display: -webkit-box;
46
+ display: -ms-flexbox;
47
+ display: flex;
48
+ -ms-flex-wrap: wrap;
49
+ flex-wrap: wrap;
50
+ max-width: 100%;
51
+ margin-top: 10vh;
52
+ margin-left: auto;
53
+ margin-right: auto;
54
+ -webkit-box-pack: center;
55
+ -ms-flex-pack: center;
56
+ justify-content: center;
57
+ }
58
+
59
+ .movie-card {
60
+ background: #ffffff;
61
+ box-shadow: 0px 6px 18px rgba(0, 0, 0, 0.1);
62
+ width: 100%;
63
+ max-width: 315px;
64
+ margin: 2em;
65
+ border-radius: 10px;
66
+ display: inline-block;
67
+ }
68
+
69
+ .movie-header {
70
+ padding: 0;
71
+ margin: 0;
72
+ height: 367px;
73
+ width: 100%;
74
+ display: block;
75
+ border-top-left-radius: 10px;
76
+ border-top-right-radius: 10px;
77
+ }
78
+
79
+ .header-icon-container {
80
+ position: relative;
81
+ }
82
+
83
+ .header-icon {
84
+ width: 100%;
85
+ height: 367px;
86
+ line-height: 367px;
87
+ text-align: center;
88
+ vertical-align: middle;
89
+ margin: 0 auto;
90
+ color: #ffffff;
91
+ font-size: 54px;
92
+ text-shadow: 0px 0px 20px #6abcea, 0px 5px 20px #6ABCEA;
93
+ opacity: .85;
94
+ }
95
+
96
+ .header-icon:hover {
97
+ background: rgba(0, 0, 0, 0.15);
98
+ font-size: 74px;
99
+ text-shadow: 0px 0px 20px #6abcea, 0px 5px 30px #6ABCEA;
100
+ border-top-left-radius: 10px;
101
+ border-top-right-radius: 10px;
102
+ opacity: 1;
103
+ }
104
+
105
+ .movie-card:hover {
106
+ -webkit-transform: scale(1.03);
107
+ transform: scale(1.03);
108
+ box-shadow: 0px 10px 25px rgba(0, 0, 0, 0.08);
109
+ }
110
+
111
+ .movie-content {
112
+ padding: 18px 18px 24px 18px;
113
+ margin: 0;
114
+ }
115
+
116
+ .movie-content-header,
117
+ .movie-info {
118
+ display: table;
119
+ width: 100%;
120
+ }
121
+
122
+ .movie-title {
123
+ font-size: 24px;
124
+ margin: 0;
125
+ display: table-cell;
126
+ }
127
+
128
+ .movie-info {
129
+ margin-top: 1em;
130
+ }
131
+
132
+ .info-section {
133
+ display: table-cell;
134
+ text-transform: uppercase;
135
+ text-align: center;
136
+ }
137
+
138
+ .info-section:first-of-type {
139
+ text-align: left;
140
+ }
141
+
142
+ .info-section:last-of-type {
143
+ text-align: right;
144
+ }
145
+
146
+ .info-section label {
147
+ display: block;
148
+ color: rgba(0, 0, 0, 0.5);
149
+ margin-bottom: .5em;
150
+ font-size: 9px;
151
+ }
152
+
153
+ .info-section span {
154
+ font-weight: 700;
155
+ font-size: 11px;
156
+ }
157
+
158
+ @media screen and (max-width: 500px) {
159
+ .movie-card {
160
+ width: 95%;
161
+ max-width: 95%;
162
+ margin: 1em;
163
+ display: block;
164
+ }
165
+
166
+ .container {
167
+ padding: 0;
168
+ margin: 0;
169
+ }
170
+ }
templates/card.html ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% for person in results %}
2
+ <style>
3
+ .image{{loop.index}} {
4
+ background: url("https://image.tmdb.org/t/p/h632/{{person.metadata.profile_path}}");
5
+ background-size: cover;
6
+ }
7
+ </style>
8
+ <div class="col">
9
+ <a href="https://www.themoviedb.org/person/{{person.metadata.id}}" style="color: inherit;">
10
+ <div class="movie-card">
11
+ <div class="movie-header image{{loop.index}}">
12
+ <!-- <div class="header-icon-container">
13
+ <a href="#">
14
+ <i class="material-icons header-icon"></i>
15
+ </a>
16
+ </div> -->
17
+ </div>
18
+ <!--movie-header-->
19
+ <div class="movie-content">
20
+ <div class="movie-content-header">
21
+ <h3 class="movie-title">{{person.metadata.name}}</h3>
22
+ </div>
23
+ <div class="movie-info">
24
+ <div class="info-section">
25
+ <label style="font-size:8.0pt">Date of Birth</label>
26
+ <span style="font-size:8.0pt">{{person.metadata.birthday}}</span>
27
+ </div>
28
+ <!--date,time-->
29
+ <div class="info-section">
30
+ <label style="font-size:8.0pt">Similarity</label>
31
+ <span style="font-size:8.0pt">{{person.score|round(2)}}</span>
32
+ </div>
33
+ <!--screen-->
34
+ <div class="info-section">
35
+ <label style="font-size:8.0pt">Popularity</label>
36
+ <span style="font-size:8.0pt">{{person.metadata.popularity|round(2)}}</span>
37
+ </div>
38
+ <!--seat-->
39
+ </div>
40
+ </div>
41
+ <!--movie-content-->
42
+ </div>
43
+ </a>
44
+ </div>
45
+ {% endfor %}