The facial recognition API that never fails

Emmanuel Kwakye Nyantakyi
6 min readMar 22, 2022

Django API with Python’s face_recognition library

Figure 1: Random image from google ;)

I discovered Django not so and long ago and since then, I have been playing around with it: making APIs for anything at all that comes to mind. In this post, I’m going to share my experience with Python’s face_recognition library and how I engineered a smarter way to use it.

Familiarising myself with the library

Before I even began the project (making the API), I had to get a feel of the library in action. My main goals were to understand how the library is used for facial recognition (the commands that get the job done), the data types involved in the process and the general performance of the algorithm. To do this, I created a python file and run several tests with some images.

Figure 2: Data table showing results for test 1

It is important to note that different images were used for the known and unknown persons. For example, Emmanuel’s image in known persons is different from that in unknown persons. Also, all the persons here are members of the same family. John is the grandfather, Emmanuel and Franklina are siblings (children of John) and Paa Kofi is a grandson of John (not the son of Franklina or Emmanuel).

Figure 3: Data table showing results for test 2

In figure 3, we compare 7 unknown persons (two pictures for Will Smith) to 5 different pictures of Will Smith.

Observations made from data collected in figures 2 and 3 and what they tell us

  1. 2.7 times out of 4, the library will produce the right results. Quantitatively, this is inaccurate because we do not have enough data but this number gives us a sense of how the algorithm performs (Only Figure 2 was used for this data).
  2. There are no red-colored Falses. This shows that false negatives are rare. Thus, the library will rarely return false if the two faces being compared are of the same person.
  3. All red-colored cells are Trues. This shows that false positives are common. In other words, it is common for the library to think two people are the same person when in fact they are different people.
  4. Faces of closely related persons are more likely to produce false positives (even if persons are of opposite sex).
  5. Quality of image does not make much of a different as long as the faces are detectable. Some images that were much clearer still produced false positives.
  6. Will Smith’s lookalike’s face matches with all the faces of Will Smith. Thus we conclude that the library is hardly able to differentiate between lookalikes.

Conclusion

About 25 to 50 percent of the time, the face_recognition library is going to produce a false positive, which is not so good. What impressed me most about this library was that it didn’t produce a single false negative: what this meant to me was that I could compare a person’s face with many other images of the same person and the result would be True through out. Using this property of the face_recognition library, I developed an algorithm that would make this library invincible.

Recommendation

Instead of comparing an unknown face to just one known face of registered persons, unknown faces should be compared to multiple known faces of each registered person. This way, we are likely to encounter at least one false positive if the unknown face is not that of the person being compared with. And from the data collected, we know that if an unknown face is that of the person it is being compared with, then we’re going to get Trues for every comparison. Essentially, we only match an unknown face to a person if results are True for each comparison of the unknown face and known faces of the person. This way we can eliminate all but false positives associated with lookalikes, which is a pretty good upgrade!

The API

The API I made basically does two main things: learn new faces and find matches for faces. In both, an HTML page is rendered, which is used to collect name of person and their image (or just image when finding a match for a face). Uploaded images in Django normally end up in media: I created a folder, images, in media to collect uploaded files. I then check if the file is an image. If it is an image, I change its name to new.jpg and then move it to a new folder called new_face inside the static folder. Using a constant name for new images allows me to know which image was just uploaded and implement my functions accordingly. After the new image has been moved to new folder, the current function redirects the url to a different one to access the function that actually learns the new face or finds a match for the new face and returns a response to the user. I created two models: Person(which I should have named People) and Item(which I should have named Faces). *i’m feeling lazy to change the names so i’m gonna keep them like that. sorry* Class Person has two variables: name_of_person and count. This class basically keeps track of each unique person and the number of faces we have of them. Inside class Person is a function increase_count(). This function increases the count variable by 1 when called. Class Item has two variables: name and image. I basically use Class Item only when a request is made to upload the image and also get the name, when necessary. I never go through the Item objects.

Learning a new face — pseudocode

if new.jpg exists in new_face directory:
load image file to a variable called image
else:
return response 'Error: No image in new_face directory'
# Check if there is a face in image and return 'No face in image' if there is no face (forgot to add this in the actual code)make the name of person whose face was uploaded lowercaseif name already exists in class Person:
increase count of that object in Person
move new.jpg to known_faces directory inside static directory
rename new.jpg to {name}{count}.jpg
else:
create new Person object
set its name_of_person to name and its count to 1
move new.jpg to known_faces directory inside static directory
rename new.jpg to {name}1.jpg
return response 'Success: New face of {name} has been added'

Finding a match for new face — pseudocode

if new.jpg exists in new_face directory:
learn new face and save encoding into variable unknown_encoding
else:
return response 'Error: No image in new_face directory'
if new.jpg does not contain a face:
return 'Error: No face detected in image'
for every Person object in database:
for every face of Person object:
learn face into known_encoding variable
compare known_encoding with unknown_encoding into result
if result is False:
break out of loop because person is not a match
if match is found:
increase count of person object
move new.jpg to known_faces directory
rename new.jpg to {name}{count}.jpg
return response 'Match found. Person is {name}'
return response 'No match found' if no match is found

Final thoughts on API

All in all, I think the API works brilliantly. If you begin by learning about 5 faces of each registered person, the API will be flawless in finding matches for faces. Additionally, after every successful match, the new face is also added to the faces of the person who was found as a match: so the API gets better the more it is used. Our current API can only fail if a find match request is sent with the image of a lookalike. The lookalike’s image will be added to the images of the person. But this will not destroy our API because we know that any new face of the person will return True when compared with the lookalike (at least that’s what our small data tells us :o).

An alternative to saving all known_images in server is to make a string of the encoding(which is a list) using f string and then saving it into the database. The encoding can then be accessed using eval. I used the former process of saving the images because it was easier for me to see what’s going on behind the scenes and also diagnose any problem that might occur.

Get the full source code for the API here.

If 10+ readers request that I make a post detailing the step by step process of making the API, I will make another post about that.

I hope you had a good read!

--

--