Skip to content

Commit f7f6f67

Browse files
ksachdevadavisking
authored andcommitted
Add python api that generates desciptor(s) from the aligned image(s) (davisking#1667)
* Add python api that generates desciptor(s) from the aligned image(s) * Remove asserts from face_recognition.py example/tutorial * In batch_compute_face_descriptors_from_aligned_images, use for-in loop to simplify the code Improvde the document on binding methods and the error message if the aligned image is not of size 150x150
1 parent 04a2387 commit f7f6f67

File tree

2 files changed

+87
-2
lines changed

2 files changed

+87
-2
lines changed

python_examples/face_recognition.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,24 @@
114114
# In particular, a padding of 0.5 would double the width of the cropped area, a value of 1.
115115
# would triple it, and so forth.
116116

117+
# There is another overload of compute_face_descriptor that can take
118+
# as an input an aligned image.
119+
#
120+
# Note that it is important to generate the aligned image as
121+
# dlib.get_face_chip would do it i.e. the size must be 150x150,
122+
# centered and scaled.
123+
#
124+
# Here is a sample usage of that
117125

126+
print("Computing descriptor on aligned image ..")
127+
128+
# Let's generate the aligned image using get_face_chip
129+
face_chip = dlib.get_face_chip(img, shape)
130+
131+
# Now we simply pass this chip (aligned image) to the api
132+
face_descriptor_from_prealigned_image = facerec.compute_face_descriptor(face_chip)
133+
print(face_descriptor_from_prealigned_image)
134+
118135
dlib.hit_enter_to_continue()
119136

120137

tools/python/src/face_recognition.cpp

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ class face_recognition_model_v1
4343
return compute_face_descriptors(img, faces, num_jitters, padding)[0];
4444
}
4545

46+
matrix<double,0,1> compute_face_descriptor_from_aligned_image (
47+
numpy_image<rgb_pixel> img,
48+
const int num_jitters
49+
)
50+
{
51+
std::vector<numpy_image<rgb_pixel>> images{img};
52+
return batch_compute_face_descriptors_from_aligned_images(images, num_jitters)[0];
53+
}
54+
4655
std::vector<matrix<double,0,1>> compute_face_descriptors (
4756
numpy_image<rgb_pixel> img,
4857
const std::vector<full_object_detection>& faces,
@@ -53,7 +62,7 @@ class face_recognition_model_v1
5362
std::vector<numpy_image<rgb_pixel>> batch_img(1, img);
5463
std::vector<std::vector<full_object_detection>> batch_faces(1, faces);
5564
return batch_compute_face_descriptors(batch_img, batch_faces, num_jitters, padding)[0];
56-
}
65+
}
5766

5867
std::vector<std::vector<matrix<double,0,1>>> batch_compute_face_descriptors (
5968
const std::vector<numpy_image<rgb_pixel>>& batch_imgs,
@@ -127,6 +136,52 @@ class face_recognition_model_v1
127136
return face_descriptors;
128137
}
129138

139+
std::vector<matrix<double,0,1>> batch_compute_face_descriptors_from_aligned_images (
140+
const std::vector<numpy_image<rgb_pixel>>& batch_imgs,
141+
const int num_jitters
142+
)
143+
{
144+
dlib::array<matrix<rgb_pixel>> face_chips;
145+
for (auto& img : batch_imgs) {
146+
147+
matrix<rgb_pixel> image;
148+
if (is_image<unsigned char>(img))
149+
assign_image(image, numpy_image<unsigned char>(img));
150+
else if (is_image<rgb_pixel>(img))
151+
assign_image(image, numpy_image<rgb_pixel>(img));
152+
else
153+
throw dlib::error("Unsupported image type, must be 8bit gray or RGB image.");
154+
155+
// Check for the size of the image
156+
if ((image.nr() != 150) || (image.nc() != 150)) {
157+
throw dlib::error("Unsupported image size, it should be of size 150x150. Also cropping must be done as `dlib.get_face_chip` would do it. \
158+
That is, centered and scaled essentially the same way.");
159+
}
160+
161+
face_chips.push_back(image);
162+
}
163+
164+
std::vector<matrix<double,0,1>> face_descriptors;
165+
if (num_jitters <= 1)
166+
{
167+
// extract descriptors and convert from float vectors to double vectors
168+
auto descriptors = net(face_chips, 16);
169+
170+
for (auto& des: descriptors) {
171+
face_descriptors.push_back(matrix_cast<double>(des));
172+
}
173+
}
174+
else
175+
{
176+
// extract descriptors and convert from float vectors to double vectors
177+
for (auto& fimg : face_chips) {
178+
auto& r = mean(mat(net(jitter_image(fimg, num_jitters), 16)));
179+
face_descriptors.push_back(matrix_cast<double>(r));
180+
}
181+
}
182+
return face_descriptors;
183+
}
184+
130185
private:
131186

132187
dlib::rand rnd;
@@ -300,6 +355,12 @@ void bind_face_recognition(py::module &m)
300355
"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. "
301356
"Optionally allows to override default padding of 0.25 around the face."
302357
)
358+
.def("compute_face_descriptor", &face_recognition_model_v1::compute_face_descriptor_from_aligned_image,
359+
py::arg("img"), py::arg("num_jitters")=0,
360+
"Takes an aligned face image of size 150x150 and converts it into a 128D face descriptor."
361+
"Note that the alignment should be done in the same way dlib.get_face_chip does it."
362+
"If num_jitters>1 then image will be randomly jittered slightly num_jitters times, each run through the 128D projection, and the average used as the face descriptor. "
363+
)
303364
.def("compute_face_descriptor", &face_recognition_model_v1::compute_face_descriptors,
304365
py::arg("img"), py::arg("faces"), py::arg("num_jitters")=0, py::arg("padding")=0.25,
305366
"Takes an image and an array of full_object_detections that reference faces in that image and converts them into 128D face descriptors. "
@@ -309,9 +370,16 @@ void bind_face_recognition(py::module &m)
309370
.def("compute_face_descriptor", &face_recognition_model_v1::batch_compute_face_descriptors,
310371
py::arg("batch_img"), py::arg("batch_faces"), py::arg("num_jitters")=0, py::arg("padding")=0.25,
311372
"Takes an array of images and an array of arrays of full_object_detections. `batch_faces[i]` must be an array of full_object_detections corresponding to the image `batch_img[i]`, "
312-
"referencing faces in that image. Every face will be converting into 128D face descriptors. "
373+
"referencing faces in that image. Every face will be converted into 128D face descriptors. "
313374
"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. "
314375
"Optionally allows to override default padding of 0.25 around the face."
376+
)
377+
.def("compute_face_descriptor", &face_recognition_model_v1::batch_compute_face_descriptors_from_aligned_images,
378+
py::arg("batch_img"), py::arg("num_jitters")=0,
379+
"Takes an array of aligned images of faces of size 150_x_150."
380+
"Note that the alignment should be done in the same way dlib.get_face_chip does it."
381+
"Every face will be converted into 128D face descriptors. "
382+
"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. "
315383
);
316384
}
317385

0 commit comments

Comments
 (0)