Building a Simple Face Recognition App with Vue.js and Kairos

Oreoluwa Ogundipe

Face Detection and Recognition have become an increasingly popular topic these days. It's a great advantage for a machine to know which user is in a picture. The applications of facial recognition in our world today are endless. From Face, iD unlock to identifying criminals on the run using real-time analysis of video feed.

What we'll build

In this article, we'll build a demo app with Kairos service in which users can upload different images of labeled faces and also try to recognize a person from an uploaded face

Training

Recognition

What is Kairos

Kairos is a leading AI engine provider which provides β€˜Human Analytics' features like Face Detection, Face Identification, Face Verification, etc. More features here. These features can be used to gather unique, real-time insights about users as they interact with your product.

Installation

The front-end part of the application is built with a Progressive Javascript Framework Vue.js and a node server on the backend which handles the interaction with Kairos API.

Dependencies

Before we begin, you need some things set up on your local machine

  • Node installed
  • Node Package Manager (npm ) installed

Once you confirm your installation, you can continue.

Step 1: Create a Kairos Account

Sign up for a free account.

After signing up, you'll be redirected to the dashboard with your credentials

PS: Note your App ID and Key ( you'll need them later )

Step 2: Set Up A Node Server

Initialize your node project and create a package.json file with:

npm init

Install necessary node modules/dependencies :

npm install fs express connect-multiparty kairos-api cors body-parser --save

fs - we need this to convert our image into a base64 mode for attachment express - we need this to enable our API routes connect-multiparty - needed to parse HTTP requests with content-type multipart/form-data kairos-api - Node SDK for Kairos cors - we need this to enable cors body-parser - we need this to attach the request body on express req object

Create an index.js file in your root directory and require the installed dependencies :

    const fs = require('fs');
    const cors = require('cors');
    const express = require('express');
    const Kairos = require('kairos-api');
    const bodyParser = require('body-parser');
    const multipart = require('connect-multiparty');

    const app = express();
    app.use(cors());
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(bodyParser.json());

    const multipartMiddleware = multipart();
    [...]

Next, configure your Kairos client in the index.js file:

// API Configurations for KAIROS
let kairo_client = new Kairos('APP_ID', 'APP_KEY');

Replace APP_ID and APP_KEY with the details from your dashboard

Add the route for uploading images to Kairos gallery. Let's call it /upload:

    [...]
    app.post('/upload', multipartMiddleware, function(req, res) {
        // get base64 version of image and send that to Kairos for training
        let base64image = fs.readFileSync(req.files.image.path, 'base64');
        var params = {
            image: base64image,
            subject_id: req.body.name,
            gallery_name: 'rekognize',
        };
        console.log('sending to Kairos for training');
        kairos_client.enroll(params).then(function(result) {
        // return status of upload
            return res.json({'status' : true });
        }).catch(function(err) { 
            // return status if upload
            return res.json({'status' : false});
        });
    });
    [...]

Add the route for recognizing a person from an uploaded face. Let's call it /verify:

    [...]
    app.post('/verify', multipartMiddleware, function(req, res) {
        // get base64 version of image and send that to Kairos for recognition
        let base64image = fs.readFileSync(req.files.image.path, 'base64');
        var params = {
            image: base64image,
            gallery_name: 'rekognize',
        };
        console.log('sending to Kairos for recognition');
        kairos_client.recognize(params).then(function(result) {
        // return the response
            return res.json(result.body);
        }).catch(function(err) { 
        // return status code as false
            return res.json({'status' : false});
        });  
    });

Once the user makes a POST request to the /upload route, the route gets the image file from the HTTP Request, converts it to a base64 version and then uploads it to Kairos with the identifier for the image and the gallery you want the image to be in. You get a JSON Response telling you whether the upload was successful or not.

Also, when the user makes a POST request to the /verify route, the route gets the image file from the HTTP Request, converts it to a base64 version and then sends it to Kairos with the gallery name for it to check if there's anyone with a similar face to the face being uploaded in the picture. Kairos then sends a JSON Response with the result of the operation, and we take further action on based on the response.

Step 3: Build the Frontend

To build the frontend, we would be using Vue.js as already mentioned earlier.

Install the Vue CLI :

    npm install -g vue-cli

Create a simple Vue project using the Vue CLI tool installed earlier:

    vue init simple facerecognizer

Inside the facerecognizer directory, create an index.html file and in the index.html file we have some basic forms that we need for the app to work.

Training

Firstly, we need a form that allows the user submit a picture of themselves to our node server and then from the server to kairos for training - for kairos to be able to recognize a face, they need to have some base images uploaded to a gallery which forms the training data for the prediction of faces for our application.

    [...]
    <form enctype="multipart/form-data" @submit.prevent="onSubmit">
        <div class="form-group">
            <label for="">Name:</label>
            <input type="text" required class="form-control" placeholder="eg Ore" name="subject_name" v-model="model.name">
        </div>
        <div class="form-group">
            <label for="">File:</label>
            <input type="file" class="form-control" accept="image/*" name="image" v-on:change="upload($event.target.files)">
        </div>
        <div class="form-group">
            <button class="btn btn-primary" >Upload</button>
            {{ loading }}
            {{ uploadStatus }}
        </div>
    </form>
    [...]

We bind the upload form to an upload event handler. Once a user selects a file, there is a showPreview method called in the Vue instance below is invoked which shows a thumbnail preview of the image about to be uploaded to Kairos.

Training

Now let's examine the Vue instance the upload form is linked to. We are going to build up our upload instance.

First, we specify element we want to bind the Vue Instance to and the data we want to render to the DOM:

    [...]
    var upload = new Vue({
        el: '#pills-upload',
        data: function() {
            return {
                model: {
                    name: '',
                    image: null,
                    item: ''
                },
                loading: '',
                uploadStatus: '',
            }
        },
    [...]

Then, we define the methods on our Vue instance. For this instance, we have the upload, showPreview and onSubmit methods.

The upload method takes the image that was uploaded, resets the uploadStatus ( this is done so that when a user is performing multiple uploads, the status is cleared before each upload ) and then calls the showPreview method:

    [...]
        methods: {
            upload: function(files) {
                this.model.image = files[0];
                this.uploadStatus = '';
                this.showPreview(files[0]);
            },
    [...]

The showPreview method is responsible for displaying a preview of the uploaded image for the user to see how it looks

    [...]
            showPreview: function(file) {
                var reader = new FileReader();
                reader.onload = function (e) {
                    document.getElementById("face_preview1").src = e.target.result;
                };
                // read the image file as a data URL.
                reader.readAsDataURL(file);
            },
    [...]

The onSubmit method is triggered when the upload button is clicked. It builds the form, populates it with data, sends a post request to the node server. When a response is received from the server, the uploadStatus is updated to let the user know if the image was successfully uploaded:

    [...]
            onSubmit: function() {
                // Assemble form data
                const formData = new FormData()
                formData.append('image', this.model.image);
                formData.append('name', this.model.name);
                this.loading = "Uploading image....Please be patient."
                // Post to server
                axios.post('http://localhost:3128/upload', formData)
                .then(res => {
                    // Post a status message
                    this.loading = '';
                    if( res.status == true){
                        this.uploadStatus = 'Image has been uploaded successfully';
                    }else{
                        this.uploadStatus = 'there was an issue with the upload, try again';
                    }
                })
            }
        }
    });

Recognition

Now we need to work on the recognition part of the app. Over here we have a form that facilitates the upload of the face picture to the server for recognition

    <form enctype="multipart/form-data" @submit.prevent="onSubmit">                       
        <div class="form-group">
            <label for="">Upload Picture of Person to Recognise:</label>
            <input type="file" class="form-control" accept="image/*" name="image" v-on:change="upload($event.target.files)">
        </div>

        <div class="form-group">
            <button class="btn btn-primary" >Rekognize</button>
            <span class="fa fa-spin fa-spinner" id="verify_spinner" style="display:none;" aria-hidden="true"></span>                            
            {{ loading }}
        </div>
    </form>

This is quite similar to the upload part; we bind the form to an event handler which makes the post request to the backend server that sends details to Kairos and gets JSON Response.

Recognition

Now we examine the Vue instance the recognize form is linked to.

First, we specify the data we want to render to the DOM.

    [...]
    var verify = new Vue({
        el: '#pills-verify',
        data: function(){
            return{
                model: {
                    image : null,
                },
                loading: '',
                resultStatus: '',
                resultDetails: '',
            }
        },
    [...]

Then, we define the methods on our Vue instance. For this instance, we have the upload,showPreview and onSubmit methods.

The upload method takes the image that was uploaded, clears the resultStatus and calls the showPreview method:

    [...]
        methods: {
            upload: function(files) {
                this.model.image = files[0];
                this.resultStatus = '';
                this.showPreview(files[0]);
            },
    [...]

The showPreview method is responsible for displaying a preview of the uploaded image for the user to see what is being sent for recognition:

    [...]
            showPreview: function(file) {
                var reader = new FileReader();
                reader.onload = function (e) {
                    document.getElementById("face_preview2").src = e.target.result;
                };
                // read the image file as a data URL.
                reader.readAsDataURL(file);
            },
    [...]

The onSubmit method is triggered when the rekognize button is clicked. It builds a form with data from the instance and sends a post request to the /verify route on the node server.

    [...]
            onSubmit: function() {
                // Assemble form data
                const formData = new FormData()
                formData.append('image', this.model.image);
                formData.append('name', this.model.name);
                this.loading = "Attempting to recognize you..please wait."
    [...]

When a response is returned from the server, we examine the response from the server and the resultStatus is updated with the name of the user if there are no errors.

    [...]
                // Post to server
                axios.post('http://localhost:3128/verify', formData)
                .then(res => {
                    // Post a status message saying the upload complete
                    this.loading = '';
                    if( !res.data.Errors){
                        if(res.data.images[0].transaction.status != "success"){
                            this.resultStatus = 'don\'t know who you are! Try uploading a picture of yourself first in upload section';
                        }else{
                            this.resultStatus = 'What\'s good ' + res.data.images[0].transaction.subject_id + '! ';
                        }
                        this.resultDetails = res.data.images[0].transaction;
                    }else{
                        this.resultStatus = 'don\'t know who you are! Try uploading a picture first in upload section';
                    }
                })
            }
        }
    })

We all know it's not every-time we Kairos will be able to successfully identify the face. In the JSON response, we check if there was an error i.e. if Kairos couldn't find a matching face and we let the user know. If a matching face is successfully found, we send a welcome message.

Result when face could not be recognized

Feel free to check out the source code here.

Conclusion

We have seen how to make a Simple Face Recognition App. The applications of this are quite numerous, you could add face authentication as one of the ways to authenticate your users, or you could also just use it to know who is interacting with your product to provide personalized experiences for your users.

Feel free to leverage the free account given to you by Kairos to give your #NextBillionUsers a great experience!

More Resources

Overlay Glasses/Masks on Avatars with Vue.js and Cloudinary