ParisNeo commited on
Commit
886b6db
1 Parent(s): fe3ff5a
Files changed (2) hide show
  1. app.py +324 -4
  2. requirements.txt +3 -0
app.py CHANGED
@@ -1,7 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
 
5
 
6
- iface = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- iface.launch()
 
1
+ """=============
2
+ Example : extract_record.py
3
+ Author : Saifeddine ALOUI (ParisNeo)
4
+ Description :
5
+ Make sure you install deepface
6
+ pip install deepface
7
+
8
+ <================"""
9
+
10
+ import numpy as np
11
+ from pathlib import Path
12
+ import cv2
13
+ import time
14
+
15
+ from FaceAnalyzer import FaceAnalyzer
16
+
17
+ from pathlib import Path
18
+ import pickle
19
+ from tqdm import tqdm # used to draw a progress bar pip install tqdm
20
+ from deepface import DeepFace
21
+
22
+ # Number of images to use to build the embedding
23
+ nb_images=50
24
+
25
+
26
+
27
+ # If faces path is empty then make it
28
+ faces_path = Path(__file__).parent/"faces"
29
+ if not faces_path.exists():
30
+ faces_path.mkdir(parents=True, exist_ok=True)
31
+
32
+
33
+ # Build face analyzer while specifying that we want to extract just a single face
34
+ fa = FaceAnalyzer(max_nb_faces=1)
35
+
36
+
37
+ box_colors=[
38
+ (255,0,0),
39
+ (0,255,0),
40
+ (0,0,255),
41
+ (255,255,0),
42
+ (255,0,255),
43
+
44
+ ]
45
+
46
+
47
  import gradio as gr
48
+ import numpy as np
49
+ class UI():
50
+ def __init__(self) -> None:
51
+ self.i=0
52
+ self.embeddings_cloud = []
53
+ self.is_recording=False
54
+ self.face_name=None
55
+ self.nb_images = 20
56
+ # Important to set. If higher than this distance, the face is considered unknown
57
+ self.threshold = 4e-1
58
+ self.faces_db_preprocessed_path = Path(__file__).parent/"faces_db_preprocessed"
59
+ self.current_name = None
60
+ self.current_face_files = []
61
+ self.draw_landmarks = True
62
+
63
+ with gr.Blocks() as demo:
64
+ gr.Markdown("## FaceAnalyzer face recognition test")
65
+ with gr.Tabs():
66
+ with gr.TabItem('Realtime Recognize'):
67
+ with gr.Blocks():
68
+ with gr.Row():
69
+ with gr.Column():
70
+ self.rt_webcam = gr.Image(label="Input Image", source="webcam", streaming=True)
71
+ with gr.Column():
72
+ self.rt_rec_img = gr.Image(label="Output Image")
73
+ self.rt_webcam.change(self.recognize, inputs=self.rt_webcam, outputs=self.rt_rec_img, show_progress=False)
74
+ with gr.TabItem('Image Recognize'):
75
+ with gr.Blocks():
76
+ with gr.Row():
77
+ with gr.Column():
78
+ self.rt_inp_img = gr.Image(label="Input Image")
79
+ with gr.Column():
80
+ self.rt_rec_img = gr.Image(label="Output Image")
81
+ self.rt_inp_img.change(self.recognize2, inputs=self.rt_inp_img, outputs=self.rt_rec_img, show_progress=True)
82
+ with gr.TabItem('Add face from webcam'):
83
+ with gr.Blocks():
84
+ with gr.Row():
85
+ with gr.Column():
86
+ self.img = gr.Image(label="Input Image", source="webcam", streaming=True)
87
+ self.txtFace_name = gr.Textbox(label="face_name")
88
+ self.txtFace_name.change(self.set_face_name, inputs=self.txtFace_name, show_progress=False)
89
+ self.status = gr.Label(label="face_name")
90
+ self.img.change(self.record, inputs=self.img, outputs=self.status, show_progress=False)
91
+ with gr.Column():
92
+ self.btn_start = gr.Button("Start Recording face")
93
+ self.btn_start.click(self.start_stop)
94
+ with gr.TabItem('Add face from files'):
95
+ with gr.Blocks():
96
+ with gr.Row():
97
+ with gr.Column():
98
+ self.gallery = gr.Gallery(
99
+ label="Uploaded Images", show_label=False, elem_id="gallery"
100
+ ).style(grid=[2], height="auto")
101
+ self.add_file = gr.Files(label="Files",file_types=["image"])
102
+ self.add_file.change(self.add_files, self.add_file, self.gallery)
103
+ self.txtFace_name2 = gr.Textbox(label="face_name")
104
+ self.txtFace_name2.change(self.set_face_name, inputs=self.txtFace_name2, show_progress=False)
105
+ self.status = gr.Label(label="face_name")
106
+ self.img.change(self.record, inputs=self.img, outputs=self.status, show_progress=False)
107
+ with gr.Column():
108
+ self.btn_start = gr.Button("Build face embeddings")
109
+ self.btn_start.click(self.start_stop)
110
+ with gr.TabItem('Known Faces List'):
111
+ with gr.Blocks():
112
+ with gr.Row():
113
+ with gr.Column():
114
+ self.faces_list = gr.Dataframe(
115
+ headers=["Face Name"],
116
+ datatype=["str"],
117
+ label="Faces",
118
+ )
119
+ with gr.Row():
120
+ with gr.Accordion(label="Options", open=False):
121
+ self.sld_threshold = gr.Slider(1e-2,10,4e-1,step=1e-2,label="Recognition threshold")
122
+ self.sld_threshold.change(self.set_th,inputs=self.sld_threshold)
123
+ self.sld_nb_images = gr.Slider(2,50,20,label="Number of images")
124
+ self.sld_nb_images.change(self.set_nb_images, self.sld_nb_images)
125
+ self.cb_draw_landmarks = gr.Checkbox(label="Draw landmarks", value=True)
126
+ self.cb_draw_landmarks.change(self.set_draw_landmarks, self.cb_draw_landmarks)
127
+
128
+ self.upgrade_faces()
129
+ demo.queue().launch()
130
+ def add_files(self, files):
131
+ for file in files:
132
+ img = cv2.cvtColor(cv2.imread(file.name), cv2.COLOR_BGR2RGB)
133
+ self.current_face_files.append(img)
134
+ return self.current_face_files
135
+
136
+ def set_th(self, value):
137
+ self.threshold=value
138
+
139
+ def set_nb_images(self, value):
140
+ self.nb_images=value
141
+
142
+ def set_draw_landmarks(self, value):
143
+ self.draw_landmarks=value
144
+
145
+ def cosine_distance(self, u, v):
146
+ """
147
+ Computes the cosine distance between two vectors.
148
+
149
+ Parameters:
150
+ u (numpy array): A 1-dimensional numpy array representing the first vector.
151
+ v (numpy array): A 1-dimensional numpy array representing the second vector.
152
+
153
+ Returns:
154
+ float: The cosine distance between the two vectors.
155
+ """
156
+ dot_product = np.dot(u, v)
157
+ norm_u = np.linalg.norm(u)
158
+ norm_v = np.linalg.norm(v)
159
+ return 1 - (dot_product / (norm_u * norm_v))
160
+
161
+ def upgrade_faces(self):
162
+ # Load faces
163
+ self.known_faces=[]
164
+ self.known_faces_names=[]
165
+ face_files = [f for f in faces_path.iterdir() if f.name.endswith("pkl")]
166
+ for file in face_files:
167
+ with open(str(file),"rb") as f:
168
+ finger_print = pickle.load(f)
169
+ self.known_faces.append(finger_print)
170
+ self.known_faces_names.append(file.stem)
171
+ self.faces_list.update(self.known_faces_names)
172
+
173
+ def set_face_name(self, face_name):
174
+ self.face_name=face_name
175
+
176
+ def start_stop(self):
177
+ self.is_recording=True
178
+
179
+ def process_db(self, images):
180
+ for i,image in enumerate(images):
181
+ # Opencv uses BGR format while mediapipe uses RGB format. So we need to convert it to RGB before processing the image
182
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
183
+ image = cv2.resize(image, (640, 480))
184
+ # Process the image to extract faces and draw the masks on the face in the image
185
+ fa.process(image)
186
+
187
+ if fa.nb_faces>0:
188
+ if fa.nb_faces>1:
189
+ print("Found too many faces!!")
190
+ face = fa.faces[0]
191
+ try:
192
+ # Get a realigned version of the landmarksx
193
+ vertices = face.get_face_outer_vertices()
194
+ image = face.getFaceBox(image, vertices,margins=(30,30,30,30))
195
+ embedding = DeepFace.represent(image)[0]["embedding"]
196
+ embeddings_cloud.append(embedding)
197
+ cv2.imwrite(str(self.faces_db_preprocessed_path/f"im_{i}.png"), cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
198
+ except Exception as ex:
199
+ print(ex)
200
+ embeddings_cloud = np.array(embeddings_cloud)
201
+ embeddings_cloud_mean = embeddings_cloud.mean(axis=0)
202
+ embeddings_cloud_inv_cov = np.linalg.inv(np.cov(embeddings_cloud.T))
203
+ # Now we save it.
204
+ # create a dialog box to ask for the subject name
205
+ name = self.face_name
206
+ with open(str(faces_path/f"{name}.pkl"),"wb") as f:
207
+ pickle.dump({"mean":embeddings_cloud_mean, "inv_cov":embeddings_cloud_inv_cov},f)
208
+ print(f"Saved {name}")
209
+
210
+ def record(self, image):
211
+ if self.face_name is None:
212
+ return "Please input a face name"
213
+ if self.is_recording and image is not None:
214
+ if self.i < self.nb_images:
215
+ # Process the image to extract faces and draw the masks on the face in the image
216
+ fa.process(image)
217
+ if fa.nb_faces>0:
218
+ try:
219
+ face = fa.faces[0]
220
+ vertices = face.get_face_outer_vertices()
221
+ image = face.getFaceBox(image, vertices, margins=(40,40,40,40))
222
+ embedding = DeepFace.represent(image)[0]["embedding"]
223
+ self.embeddings_cloud.append(embedding)
224
+ self.i+=1
225
+ cv2.imshow('Face Mesh', cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
226
+ except Exception as ex:
227
+ print(ex)
228
+ return f"Processing frame {self.i}/{self.nb_images}..."
229
+ else:
230
+ # Now let's find out where the face lives inside the latent space (128 dimensions space)
231
+
232
+ embeddings_cloud = np.array(self.embeddings_cloud)
233
+ embeddings_cloud_mean = embeddings_cloud.mean(axis=0)
234
+ embeddings_cloud_inv_cov = embeddings_cloud.std(axis=0)
235
+ # Now we save it.
236
+ # create a dialog box to ask for the subject name
237
+ name = self.face_name
238
+ with open(str(faces_path/f"{name}.pkl"),"wb") as f:
239
+ pickle.dump({"mean":embeddings_cloud_mean, "inv_cov":embeddings_cloud_inv_cov},f)
240
+ print(f"Saved {name} embeddings")
241
+ self.i=0
242
+ self.embeddings_cloud=[]
243
+ self.is_recording=False
244
+ self.upgrade_faces()
245
+
246
+ return f"Saved {name} embeddings"
247
+ else:
248
+ return "Waiting"
249
+
250
+ def recognize(self, image):
251
+ # Process the image to extract faces and draw the masks on the face in the image
252
+ fa.process(image)
253
+
254
+ if fa.nb_faces>0:
255
+ for i in range(fa.nb_faces):
256
+ try:
257
+ face = fa.faces[i]
258
+ vertices = face.get_face_outer_vertices()
259
+ face_image = face.getFaceBox(image, vertices, margins=(40,40,40,40))
260
+ embedding = DeepFace.represent(face_image)[0]["embedding"]
261
+ if self.draw_landmarks:
262
+ face.draw_landmarks(image, color=(0,0,0))
263
+ nearest_distance = 1e100
264
+ nearest = 0
265
+ for i, known_face in enumerate(self.known_faces):
266
+ # absolute distance
267
+ distance = np.abs(known_face["mean"]-embedding).sum()
268
+ # euclidian distance
269
+ #diff = known_face["mean"]-embedding
270
+ #distance = np.sqrt(diff@diff.T)
271
+ # Cosine distance
272
+ distance = self.cosine_distance(known_face["mean"], embedding)
273
+ if distance<nearest_distance:
274
+ nearest_distance = distance
275
+ nearest = i
276
+
277
+ if nearest_distance>self.threshold:
278
+ face.draw_bounding_box(image, thickness=1,text=f"Unknown:{nearest_distance:.3e}")
279
+ else:
280
+ face.draw_bounding_box(image, thickness=1,text=f"{self.known_faces_names[nearest]}:{nearest_distance:.3e}")
281
+ except Exception as ex:
282
+ pass
283
+
284
+ # Return the resulting frame
285
+ return image
286
+
287
+ def recognize2(self, image):
288
+ if image is None:
289
+ return None
290
+ image = cv2.resize(image, fa.image_size)
291
+ # Process the image to extract faces and draw the masks on the face in the image
292
+ fa.process(image)
293
+
294
+ if fa.nb_faces>0:
295
+ for i in range(fa.nb_faces):
296
+ try:
297
+ face = fa.faces[i]
298
+ vertices = face.get_face_outer_vertices()
299
+ face_image = face.getFaceBox(image, vertices, margins=(40,40,40,40))
300
+ embedding = DeepFace.represent(face_image)[0]["embedding"]
301
+ if self.draw_landmarks:
302
+ face.draw_landmarks(image, color=(0,0,0))
303
+ nearest_distance = 1e100
304
+ nearest = 0
305
+ for i, known_face in enumerate(self.known_faces):
306
+ # absolute distance
307
+ distance = np.abs(known_face["mean"]-embedding).sum()
308
+ # euclidian distance
309
+ #diff = known_face["mean"]-embedding
310
+ #distance = np.sqrt(diff@diff.T)
311
+ # Cosine distance
312
+ distance = self.cosine_distance(known_face["mean"], embedding)
313
+ if distance<nearest_distance:
314
+ nearest_distance = distance
315
+ nearest = i
316
+
317
+ if nearest_distance>self.threshold:
318
+ face.draw_bounding_box(image, thickness=1,text=f"Unknown:{nearest_distance:.3e}")
319
+ else:
320
+ face.draw_bounding_box(image, thickness=1,text=f"{self.known_faces_names[nearest]}:{nearest_distance:.3e}")
321
+ except Exception as ex:
322
+ pass
323
 
324
+ # Return the resulting frame
325
+ return image
326
+ ui = UI()
327
 
 
 
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ deepface
2
+ opencv
3
+ FaceAnalyzer