-
Notifications
You must be signed in to change notification settings - Fork 1
/
my_dlib_funcs.py
353 lines (250 loc) · 13.4 KB
/
my_dlib_funcs.py
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# -*- coding: utf-8 -*-
"""
The Python module contains a suite of functions for
working with the dlib library for face recognition.
The functions allow for reading and writing face data to and from a CSV database,
extracting faces from images, and performing recognition tasks.
"""
import os
import csv
import dlib
import cv2
import numpy as np
dlib.DLIB_USE_CUDA = False
# ****************************************************************************************
def calculate_distance(descriptor1 = None ,descriptor2 = None):
"""
Calculates the Euclidean distance between two face descriptors.
The computed distance represents the degree of similarity between two faces.
Args:
descriptor1 (list): The first face descriptor, represented as a list of numbers.
descriptor2 (list): The second face descriptor, represented as a list of numbers.
Returns:
float: The Euclidean distance between the two face descriptors.
"""
return np.linalg.norm(np.array(descriptor1) - np.array(descriptor2))
# ****************************************************************************************
def get_face_descriptors(frame= None ,
detection_scheme = 'cnn',
face_detector_path = None,
shape_predictor = None,
face_recognizer = None ,
num_jitters = 1,
upsampling = 1 ):
"""Compute face descriptors for faces in an image.
Args:
frame: a numpy array representing an image.
detection_scheme: a string indicating the face detection scheme to be
used, either 'cnn' or 'HOG'.
face_detector_path: the path to the cnn face detection model.
shape_predictor: a dlib shape predictor object.
face_recognizer: a dlib face recognizer object.
num_jitters: If num_jitters>1 then each face will be randomly jittered slightly num_jitters
times, each run through the 128D projection, and the average used as the face descriptor.
upsampling: the upsampling factor to be used in the HOG face detection
scheme.
Returns:
A list of dictionaries, each containing two items:
- 'face descriptor': a numpy array representing the 128-dimensional face
descriptor.
- 'bounding box': the bounding box of the face in the image.
Raises:
None
"""
face_descriptors = []
if detection_scheme == 'cnn':
face_detector = dlib.cnn_face_detection_model_v1(face_detector_path)
faces = face_detector(frame)
print("Number of faces detected: {}".format(len(faces)))
for i, d in enumerate(faces):
cache = {}
shape = shape_predictor(frame, d.rect)
face_descriptor = face_recognizer.compute_face_descriptor(frame, shape ,
num_jitters =num_jitters)
cache["face descriptor"] = face_descriptor
left = d.rect.left()
top = d.rect.top()
right = d.rect.right()
bottom = d.rect.bottom()
cache["bounding box"] = (left, top, right, bottom)
face_descriptors.append(cache)
return face_descriptors
if detection_scheme == 'HOG':
face_detector = dlib.get_frontal_face_detector()
faces = face_detector(frame,upsampling)
print("Number of faces detected: {}".format(len(faces)))
for i, d in enumerate(faces):
cache = {}
shape = shape_predictor(frame, d)
face_descriptor = face_recognizer.compute_face_descriptor(frame, shape,
num_jitters = num_jitters)
cache["face descriptor"] = face_descriptor
left = d.left()
top = d.top()
right = d.right()
bottom = d.bottom()
cache["bounding box"] = (left, top, right, bottom)
face_descriptors.append(cache)
return face_descriptors
# ****************************************************************************************
def get_database_face_descriptors(database_path = '',
detection_scheme = 'cnn',
face_detector_path = None,
shape_predictor = None,
face_recognizer = None ,
num_jitters = 10,
upsampling = 1):
"""This function is used to obtain face descriptors for all images in a given database path.
Args:
- database_path: str, the path to the directory that contains the images.
- detection_scheme: str, the detection scheme to be used either "cnn" or "HOG".
- face_detector_path: the path to the cnn face detector, required only if the detection_scheme is "cnn".
- shape_predictor: a dlib shape predictor, required for both detection_schemes.
- face_recognizer: a dlib face recognizer.
- num_jitters: If num_jitters>1 then each face will be randomly jittered slightly num_jitters
times, each run through the 128D projection, and the average used as the face descriptor.
- upsampling: int, the number of times to upsample the image prior to applying face detection, required only if the detection_scheme is "HOG".
Returns:
- db_descriptors: list, a list of dictionaries, each dictionary contain the following keys:
- face descriptor: numpy array, the face descriptor.
- bounding box: dlib rect, the bounding box of the face in the image.
- name: str, the name of the person.
- img path: str, the path to the image in the database.
"""
db_descriptors = []
for i,f in enumerate(os.listdir(database_path)): #☺ **************************************
img = dlib.load_rgb_image(database_path +'/' + f)
face_descriptors = get_face_descriptors(img,
detection_scheme = detection_scheme,
face_detector_path = face_detector_path,
shape_predictor = shape_predictor,
face_recognizer = face_recognizer ,
num_jitters = num_jitters,
upsampling = 1)
face_descriptors = face_descriptors[0]
face_descriptors['name']= f[:-4]
face_descriptors['img path']= database_path +'/' + f
db_descriptors.append(face_descriptors)
return db_descriptors
# ****************************************************************************************
def recognize(target_descriptors = None, database_descriptors = None, max_dist_thresh = 0.55 ):
"""Recognize faces in the target descriptors.
Given the target descriptors and database descriptors, the function finds the
best match and assigns the name to the target descriptors if the distance between
them is less than the maximum distance threshold.
Args:
target_descriptors: A list of dictionaries, each containing the face descriptor
and bounding box of the target face.
database_descriptors: A list of dictionaries, each containing the face descriptor
and name of the database face.
max_dist_thresh: A float, representing the maximum distance threshold for face
recognition. A face is considered recognized if the distance between the
target face descriptor and the database face descriptor is less than the
threshold.
Returns:
None
"""
for target_descriptor in target_descriptors:
distances = []
target_descriptor["name"] = 'UNKNOWN'
for database_descriptor in database_descriptors:
dist = calculate_distance(np.array(target_descriptor["face descriptor"]),
np.array(database_descriptor["face descriptor"]))
distances.append(dist)
if distances:
idx = np.argmin(distances)
print(distances[idx])
if distances[idx] < max_dist_thresh:
target_descriptor["name"] = database_descriptors[idx]["name"]
# ****************************************************************************************
def save_db_to_csv(filename = '', db_face_descriptors = None):
"""Saves the given database of face descriptors to a CSV file.
Args:
filename: A string that specifies the name of the output CSV file. If no
filename is provided, the default value of an empty string will be used.
db_face_descriptors: A list of dictionaries that represent the face
descriptors in the database. Each dictionary should have the same keys.
Returns:
None
"""
header = list(db_face_descriptors[0].keys())
with open(filename, 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(header)
rows = [db_face_descriptor.values() for db_face_descriptor in db_face_descriptors]
writer.writerows(rows)
# ******************************************************************************************
def read_db_csv(filename = ''):
"""Reads a CSV file that represents a database of face descriptors.
Args:
filename: A string that specifies the name of the input CSV file. If no
filename is provided, the default value of an empty string will be used.
Returns:
A list of dictionaries that represent the face descriptors in the database.
Each dictionary will have the following keys: "bounding box", "face descriptor".
"bounding box" will be a tuple of integers, and "face descriptor" will be a numpy array of floats.
"""
rows = []
with open(filename, 'r') as f:
reader = csv.DictReader(f)
for row in reader:
row["bounding box"] = tuple(map(int,tuple(row["bounding box"][1:-1].split(', '))))
str_pts = row["face descriptor"].split('\n')
row["face descriptor"] = np.array([float(str_pt) for str_pt in str_pts])
rows.append(row)
return rows
# **********************************************************************************************
def update_database_descriptors(database_path = '',
csv_file = '',
detection_scheme = 'cnn',
face_detector_path = '',
shape_predictor = None,
face_recognizer = None ,
num_jitters = 10,
upsampling = 1 ):
"""
Updates the descriptor information of the faces in the database.
Args:
database_path: Path to the folder that contains the images of the people
to be added to the database.
csv_file: Path to the csv file that contains the descriptor
information of the people in the database.
detection_scheme: The method to use for face detection.
face_detector_path: Path to the face detector model file.
shape_predictor: A dlib shape predictor object.
face_recognizer: A dlib face recognition model object.
num_jitters: Number of times to perform face alignment.
upsampling: Number of times to upsample the image.
Returns:
None
"""
db_descriptors = []
rows = read_db_csv(filename = csv_file)
print(len(rows))
csv_names = [row["name"] for row in rows]
img_names = [img_name[:-4] for img_name in os.listdir(database_path)]
csv_names_paths = [row["img path"] for row in rows]
img_names_paths = {img_name[:-4] : img_name for img_name in os.listdir(database_path)}
print(csv_names)
print(img_names)
if len(img_names) > len(csv_names):
people_to_add = set(img_names) - set(csv_names)
print(f"Adding {len(people_to_add)} new people to {csv_file}")
for img_name in people_to_add:
img_path = img_names_paths[img_name]
img = dlib.load_rgb_image(database_path +'/' + img_path)
face_descriptors = get_face_descriptors(img,
detection_scheme = detection_scheme,
face_detector_path = face_detector_path,
shape_predictor = shape_predictor,
face_recognizer = face_recognizer ,
num_jitters = num_jitters,
upsampling = 1)
face_descriptors = face_descriptors[0]
face_descriptors['name']= img_name
face_descriptors['img path']= database_path +'/' + img_path
db_descriptors.append(face_descriptors)
with open(csv_file, 'a', newline='') as file:
writer = csv.writer(file)
rows = [db_face_descriptor.values() for db_face_descriptor in db_descriptors]
writer.writerows(rows)