Face Liveness on Web Browser

We implemented our Face SDK on the web front end via Javascript

Please check the following link: https://face-capture-web.dmsworkspace.com/

main.js

const PITCH_THRESHOLD = 7;
const YAW_THRESHOLD = 7;
const ROLL_THRESHOLD = 13;
const MASK_THRESHOLD = 0.5;
const SUNGLASS_THRESHOLD = 0.5;
const EYE_DIST_THRESHOLD_MIN = 90;
const EYE_DIST_THRESHOLD_MAX = 150;
const BLURRINESS_THRESHOLD = 20;
const LUMINANCE_LOW_THRESHOLD = 50;
const LUMINANCE_HIGH_THRESHOLD = 200;
const EYE_CLOSE_THRESHOLD = 0.8;
const LIVENESS_THRESHOLD = 0.5;

var CAM_WIDTH = 640;
var CAM_HEIGHT = 480;
const CENTER_R = 190;

var best_yaw = 0;
var best_pitch = 0;
var best_roll = 0;
var best_blurriness = 0;
var brisque_count = 0;

var Module = {};

var wasmModuleLoaded = false;
var wasmModuleLoadedCallbacks = [];

Module.onRuntimeInitialized = function() {
    wasmModuleLoaded = true;
    for (var i = 0; i < wasmModuleLoadedCallbacks.length; i++) {
        wasmModuleLoadedCallbacks[i]();
    }
    document.getElementById("camera").disabled = false;
}

fetch('liveface.wasm')
.then(response => response.arrayBuffer())
.then(buffer => {
    Module.wasmBinary = buffer;
    var script = document.createElement('script');
    script.src = 'liveface.js';
    script.onload = function() {
        console.log('Emscripten boilerplate loaded1.');
    }
    document.body.appendChild(script);
});


function send_image(photo) {
    var url = "https://dmsworkspace-gateway-backend-int.dmsworkspace.com/face-liveness-sdk/v1/check_liveness_base64";
  
    var xhr = new XMLHttpRequest();
    xhr.open("POST", url);
  
    xhr.setRequestHeader("Accept", "application/json");
    xhr.setRequestHeader("Content-Type", "application/json");
  
    xhr.onreadystatechange = function () {
        console.log('onreadystatechange ' + xhr.readyState + " " + xhr.status);
        if (xhr.readyState === 4 && xhr.status === 200) {
            var json = JSON.parse(xhr.responseText);
            console.log(json);
        }
    };
  
    var data = `{
      "base64": "` + photo + `"
    }`;
  
    xhr.send(data);
}

function checkFaceQuality() {
    const video = document.getElementById("inputVideo");
    const canvas1 = document.getElementById("capture1");
    canvas1.width = video.videoWidth;
    canvas1.height = video.videoHeight;
    canvas1.getContext('2d').drawImage(video, 0, 0, canvas1.width, canvas1.height);

    var imageData = canvas1.getContext('2d').getImageData(0, 0, canvas1.width, canvas1.height);
    var data = imageData.data;

    dst = _malloc(data.length);
    HEAPU8.set(data, dst);

    // max 20 objects
    resultarray = new Float32Array(21);
    resultbuffer = _malloc(21 * Float32Array.BYTES_PER_ELEMENT);

    HEAPF32.set(resultarray, resultbuffer / Float32Array.BYTES_PER_ELEMENT);

    _process(dst, canvas1.width, canvas1.height, resultbuffer);

    // resultarray
    var qaqarray = HEAPF32.subarray(resultbuffer / Float32Array.BYTES_PER_ELEMENT, resultbuffer / Float32Array.BYTES_PER_ELEMENT + 143);

    var count = qaqarray[0];
    var bbox_x = qaqarray[1];
    var bbox_y = qaqarray[2];
    var bbox_w = qaqarray[3];
    var bbox_h = qaqarray[4];

    var msg = "";

    if (count == 0) {
        msg = "No Person Detected"
    } else if(count > 1) {
        msg = "Multiple Person Detected"
    } else {
        var center_rect_x1 = (CAM_WIDTH / 2) - CENTER_R * 0.9;
        var center_rect_y1 = (CAM_HEIGHT / 2) - CENTER_R;
        var center_rect_x2 = (CAM_WIDTH / 2) + CENTER_R * 0.9;
        var center_rect_y2 = (CAM_HEIGHT / 2) + CENTER_R * 1.1;

        if(!(bbox_x >= center_rect_x1 && bbox_y >= center_rect_y1 && bbox_x < center_rect_x2 && bbox_y < center_rect_y2 &&
            bbox_x + bbox_w >= center_rect_x1 && bbox_y + bbox_h >= center_rect_y1 && bbox_x + bbox_w < center_rect_x2 && bbox_y + bbox_h < center_rect_y2)) {
            msg = "Move to center";
            brisque_count = 0;
        }
        // else if(Math.abs(qaqarray[4 + 1]) > YAW_THRESHOLD) {//yaw
        //     msg = "Look Straight";
        //     brisque_count = 0;
        // } 
        // else if(Math.abs(qaqarray[5 + 1]) > PITCH_THRESHOLD) {//pitch
        //     msg = "Look Straight";
        //     brisque_count = 0;
        // }
        // else if(Math.abs(qaqarray[6 + 1]) > ROLL_THRESHOLD) {//roll
        //     msg = "Look Straight";
        //     brisque_count = 0;
        // }
        else if(qaqarray[9 + 1] > MASK_THRESHOLD) {//mask
            msg = "Mask Detected";
            brisque_count = 0;
        }
        else if(qaqarray[10 + 1] > SUNGLASS_THRESHOLD) {//sunglass
            msg = "Sunglass Detected";
            brisque_count = 0;
        }
        else if(qaqarray[11 + 1] < EYE_CLOSE_THRESHOLD) {//eyeclose
            msg = "Eye Closed";
            brisque_count = 0;
        }
        else if(qaqarray[8 + 1] < EYE_DIST_THRESHOLD_MIN) {//eyedist
            msg = "Move Closer";
            brisque_count = 0;
        } else if(qaqarray[8 + 1] > EYE_DIST_THRESHOLD_MAX) {//eyedist
            msg = "Go back";
            brisque_count = 0;
        }
        else if(qaqarray[17 + 1] < BLURRINESS_THRESHOLD) {//brisque
            msg = "Hold Still";
            brisque_count = 0;
        }
        else if(qaqarray[16 + 1] < LUMINANCE_LOW_THRESHOLD) {//liveness
            msg = "Low Luminance";
            brisque_count = 0;
        }
        else if(qaqarray[16 + 1] > LUMINANCE_HIGH_THRESHOLD) {//liveness
            msg = "High Luminance";
            brisque_count = 0;
        }
        // else if(qaqarray[18 + 1] < LIVENESS_THRESHOLD) {//liveness
        //     msg = "Spoof Face";
        //     brisque_count = 0;
        // }
        else{
            msg = "Selfie OK";

            if(best_blurriness == 0) {
                best_blurriness = qaqarray[17 + 1];
                best_yaw = Math.abs(qaqarray[4 + 1]);
                best_pitch = Math.abs(qaqarray[5 + 1]);
                best_roll = Math.abs(qaqarray[6 + 1]);


                // // Create a JSON object containing the image data
                // const imageJson = {
                //     file: imageDataURL
                // };

                // // Convert the JSON object to a JSON string
                // const jsonString = JSON.stringify(imageJson);
                
                // // Make a POST request to the API
                // fetch('https://api.kby-ai.com/check_liveness_base64', {
                //     method: 'POST',
                //     headers: {
                //     'Content-Type': 'application/json'
                //     },
                //     body: JSON.stringify(imageJson)
                // })
                // .then(response => response.json())
                // .then(data => {
                //     console.log('API response:', data);
                // })
                // .catch(error => {
                //     console.error('Error:', error);
                // });
            } else {
                var is_better = 0;
                if(qaqarray[17 + 1] < best_blurriness) {
                    is_better = 0;
                } else {
                    is_better = 1;

                    best_blurriness = qaqarray[17 + 1];
                    best_yaw = Math.abs(qaqarray[4 + 1]);
                    best_pitch = Math.abs(qaqarray[5 + 1]);
                    best_roll = Math.abs(qaqarray[6 + 1]);

                   
                    const video = document.getElementById("capture1");
                    const canvas = document.getElementById("best_capture");
                    canvas.width = video.width
                    canvas.height = video.height    
                    canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);    
                }
            }
            
            brisque_count ++;
            const cb = document.querySelector('#autoCapture');
            if(cb.checked && brisque_count >= 5) {
                const video = document.getElementById("best_capture");
                const canvas = document.getElementById("capture");
                canvas.style.opacity="1.0";
                canvas.width = video.width
                canvas.height = video.height
                canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
               
                const videoEl = document.getElementById('inputVideo')
                videoEl.srcObject = null
                brisque_count = 0;

                document.getElementById('camera').innerText = "Start Camera";
                document.getElementById("face_cover").style.visibility = "hidden";

                const imageDataURL = canvas.toDataURL('image/png');
                const base64Data = imageDataURL.split(',')[1];
                send_image(base64Data);
            }
        }
    }
    
    document.getElementById("cap_message").innerHTML = msg;
    document.getElementById("res_yaw").innerHTML = "Yaw: " + qaqarray[4 + 1];
    // document.getElementById("res_yaw").innerHTML = video.videoWidth;
    document.getElementById("res_pitch").innerHTML = "Pitch: " + qaqarray[5 + 1];
    // document.getElementById("res_pitch").innerHTML = video.videoHeight;
    document.getElementById("res_roll").innerHTML = "Roll: " + qaqarray[6 + 1];
    document.getElementById("res_eyeDist").innerHTML = "Eye Dist: " + qaqarray[8 + 1];
    document.getElementById("res_eyeClosed").innerHTML = "Eye Closed: " + qaqarray[11 + 1];
    document.getElementById("res_mask").innerHTML = "Mask: " + qaqarray[9 + 1];
    document.getElementById("res_sunglass").innerHTML = "Sunglass: " + qaqarray[10 + 1];
    document.getElementById("res_leftEye").innerHTML = "Left Eye: (" + qaqarray[12 + 1] + ", " + qaqarray[13 + 1] + ")";
    document.getElementById("res_rightEye").innerHTML = "Right Eye: (" + qaqarray[14 + 1] + ", " + qaqarray[15 + 1] + ")";
    document.getElementById("res_brisque").innerHTML = "Face Brisque: " + qaqarray[7 + 1];
    document.getElementById("res_luminance").innerHTML = "Face Luminance: " + qaqarray[16 + 1];
    document.getElementById("res_blurriness").innerHTML = "Face Blurriness: " + qaqarray[17 + 1];
    // document.getElementById("res_liveness").innerHTML = "Face Liveness: " + qaqarray[18 + 1];
    
    _free(resultbuffer);
    _free(dst);
}

async function onPlay() {
    const videoEl = document.getElementById('inputVideo')

    if (videoEl.paused || videoEl.ended)
        return setTimeout(() => onPlay())

    const image = document.getElementById("face_cover");
    const canvas = document.getElementById("capture");
    canvas.width = image.width;
    canvas.height = image.height;
    canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height);
    document.getElementById("capture").style.opacity = "0.5"

    checkFaceQuality();
  
    setTimeout(() => onPlay())
}

async function startCamera() {  

    const stream = await navigator.mediaDevices.getUserMedia({ video: {} })
    const videoEl = document.getElementById('inputVideo')
    if(videoEl.srcObject == null) {
        document.getElementById('camera').innerText = "Stop Camera";    
        
        videoEl.srcObject = stream  
        old_liveness1 = 0;
        old_liveness2 = 0;
        brisque_count = 0;

    } else {
        const canvas = document.getElementById("capture");
        canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
    
        videoEl.srcObject = null  
        document.getElementById('camera').innerText = "Start Camera";
    }
}

function isMobile() {
    let check = false;
    (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
    return check;
    return true;
}


function load() {
    if(isMobile()) {

      document.getElementById("inputVideo").style.width = '720px'
      document.getElementById("inputVideo").style.height = '960px'

      document.getElementById("capture").style.width = '720px'
      document.getElementById("capture").style.height = '960px'

      document.getElementById("face_cover").style.width = '720px'
      document.getElementById("face_cover").style.height = '960px'

      document.getElementById("capture1").style.width = '720px'
      document.getElementById("capture1").style.height = '960px'

      document.getElementById("best_capture").style.width = '720px'
      document.getElementById("best_capture").style.height = '960px'

      document.getElementById("div_video").style.width = '720px'
      document.getElementById("div_video").style.height = '960px'

      document.getElementById("face_cover").src = "face_cover_p.png";

      CAM_WIDTH = 480;
      CAM_HEIGHT = 640;
    } else {
      document.getElementById("inputVideo").style.width = '640px'
      document.getElementById("inputVideo").style.height = '480px'

      document.getElementById("capture").style.width = '640px'
      document.getElementById("capture").style.height = '480px'

      document.getElementById("face_cover").style.width = '640px'
      document.getElementById("face_cover").style.height = '480px'

      document.getElementById("capture1").style.width = '640px'
      document.getElementById("capture1").style.height = '480px'
      
      document.getElementById("best_capture").style.width = '640px'
      document.getElementById("best_capture").style.height = '480px'

      document.getElementById("div_video").style.width = '640px'
      document.getElementById("div_video").style.height = '480px'

      CAM_WIDTH = 640;
      CAM_HEIGHT = 480;
    }
  }
window.onload = load;

Git Repository

Here you can find the whole code sample to check how to use our FaceLiveness SDK

https://dev.azure.com/hillogy/_git/facecapture-web

Last updated