In this article we will explain how to implement a reliable face recognition system using face-recognition.js . We were looking for a suitable Node.js library that could accurately recognize faces, but found nothing. I had to write on my own! ')
This npm package uses the dlib library, which provides Node.js bindings for very proven recognition tools inside this library. Dlib uses deep learning methods and comes with already trained models that demonstrate recognition accuracy of99.38% when running the LFW benchmark .
What for?
Recently, we are trying to create a face recognition application based on Node.js, which would highlight and recognize faces of characters from the Big Bang Theory series. At first it was planned to make an application using OpenCV recognition tools, as described in the Node.js + OpenCV for Face Recognition article.
However, despite the high speed, the quality of work of these tools turned out to be insufficient. More precisely, they coped well with full-face faces, but as soon as a person turned away from the camera a little, the quality of recognition decreased.
In search of a solution, C + + - the dlib library came across; we fiddled with the Python API, were impressed with the result and finally decided: we will use this library together with Node.js ! So this npm-package was born, providing a simplified Node.js API for face recognition.
And what is face-recognition.js?
I wanted to make a package based on face-recogntion.js, which:
allows you to quickly begin to recognize faces through a simple API;
if necessary, allows for fine tuning;
easy to install (ideally, it would be enough to write npm install ).
Although the package is not finished yet, you can already download some tools.
Face Detection
For quick and not very reliable identification of faces in an image, you can use a deep learning neural network or a simple front-end recognizer:
Face Recognizer
The recognizer is a deep learning neural network that uses the models mentioned above to compute unique face descriptors. You can train the recognizer on an array of tagged face images, after which it will be able to mark faces on the input image:
Face Landmarks
With this package you can also define from 5 to 68 reference points on the faces:
Great story, show now how it works!
So, to solve the problem using OpenCV failed. We still have a pack of faces of Sheldon, Rajesh, Leonard, Howard, and Stewart, each 150 x 150 pixels in size. Using this data, you can easily teach Face Recognizer to recognize new faces. The code for this example is in the repository.
Data preparation
We collected about 20 faces of each character in different angles:
Take 10 individuals for training, and use the rest to assess the accuracy of recognition:
This code feeds the faces of the neural network, which gives a handle to each person and stores it in the appropriate class. By numJitters third argument to numJitters , you can apply rotation , scaling, and mirroring , creating different versions of each of the input entities. Increasing the number of modifications can improve recognition accuracy, but at the same time, the neural network is learning longer.
You can also save the state of the recognizer, so as not to train it again each time, but simply load from the file:
Now, using the control data, we will check the recognition accuracy and save the results to the log:
const errors = classNames.map(_ => []) testDataByClass.forEach((faces, label) => { const name = classNames[label] console.log() console.log('testing %s', name) faces.forEach((face, i) => { const prediction = recognizer.predictBest(face) console.log('%s (%s)', prediction.className, prediction.distance) // count number of wrong classifications if (prediction.className !== name) { errors[label] = errors[label] + 1 } }) }) // print the result const result = classNames.map((className, label) => { const numTestFaces = testDataByClass[label].length const numCorrect = numTestFaces - errors[label].length const accuracy = parseInt((numCorrect / numTestFaces) * 10000) / 100 return `${className} ( ${accuracy}% ) : ${numCorrect} of ${numTestFaces} faces have been recognized correctly` }) console.log('result:') console.log(result)
Now the recognition is performed as follows: first, the Euclidean distance of the descriptor vector to each class descriptor is calculated at the input face, and then the average value of all distances is calculated. You may argue that for this task clustering using the k-means method or the SVM classifier is better suited. Perhaps in the future they will also be implemented, but the speed and efficiency of the Euclidean distance is still quite sufficient.
By calling predictBest we get the result with the smallest Euclidean distance, that is, with the greatest similarity. Like that:
{ className: 'sheldon', distance: 0.5 }
If you need for a particular person to get the distance of the descriptors of all classes, you can simply use recognizer.predict(image) , which for each class will produce arrays of distances:
If to execute the above-stated code, we will receive such results.
We train on 10 faces of each character:
sheldon ( 90.9% ) : 10of11 faces have been recognized correctly lennard ( 100% ) : 12of12 faces have been recognized correctly raj ( 100% ) : 12of12 faces have been recognized correctly howard ( 100% ) : 12of12 faces have been recognized correctly stuart ( 100% ) : 3of3 faces have been recognized correctly
We train only on 5 faces of each character:
sheldon ( 100% ) : 16of16 faces have been recognized correctly lennard ( 88.23% ) : 15of17 faces have been recognized correctly raj ( 100% ) : 17of17 faces have been recognized correctly howard ( 100% ) : 17of17 faces have been recognized correctly stuart ( 87.5% ) : 7of8 faces have been recognized correctly
And this is how it looks in the video:
Conclusion
Judging by the results, even a small sample of training data allows for fairly accurate recognition. This is despite the fact that some of the input images are very blurred due to their small size.