Source: trackingjsGaze.js

define('TrackingjsGaze', function() {
    "use strict"

    /**
     * TrackingjsGaze constructor which uses the trackingjs library in order to find the head and eye positions
     * @alias module:TrackingjsGaze
     * @exports TrackingjsGaze 
     */
    var TrackingjsGaze = function() {

    }

    /**
     * Isolates the two patches that correspond to the user's eyes
     * @param  {Canvas} imageCanvas - canvas corresponding to the webcam stream
     * @param  {number} width - of imageCanvas
     * @param  {number} height - of imageCanvas
     * @return {Object} the two eye-patches, first left, then right eye
     */
    TrackingjsGaze.prototype.getEyePatches = function(imageCanvas, width, height) {

        if (imageCanvas.width == 0) {
            return null;
        }

        //current ImageData that correspond to the working image. 
        //It can be the whole canvas if the face detection failed or only the upper half of the face to avoid unnecessary computations
        var workingImage = imageCanvas.getContext('2d').getImageData(0,0,width,height);

        var face = this.detectFace(workingImage, width, height);

        //offsets of the working image from the top left corner of the video canvas
        var offsetX = 0;
        var offsetY = 0;

        //if face has been detected
        if (face.length>0 && !isNaN(face[0]) && !isNaN(face[1]) && !isNaN(face[2]) && !isNaN(face[3])){
            //working image is restricted on upper half of detected face
            workingImage = imageCanvas.getContext('2d').getImageData(Math.floor(face[0]), Math.floor(face[1]), Math.floor(face[2]), Math.floor(face[3]/2));
            width = Math.floor(face[2]);
            height = Math.floor(face[3]/2);
            //offset from detected face
            offsetX = Math.floor(face[0]);
            offsetY = Math.floor(face[1]);  
        }

        var eyes = this.detectEyes(workingImage, width, height);
        console.log(eyes);
        if (eyes == null){
            return null;
        }

        var eyeObjs = {};
        var leftImageData = imageCanvas.getContext('2d').getImageData(Math.floor(eyes[0][0])+offsetX, Math.floor(eyes[0][1])+offsetY, Math.floor(eyes[0][2]), Math.floor(eyes[0][3]));
        eyeObjs.left = {
            patch: leftImageData,
            imagex: eyes[0][0]+offsetX,
            imagey: eyes[0][1]+offsetY,
            width: eyes[0][2],
            height: eyes[0][3]
        };
 
        var rightImageData = imageCanvas.getContext('2d').getImageData(Math.floor(eyes[1][0])+offsetX, Math.floor(eyes[1][1])+offsetY, Math.floor(eyes[1][2]), Math.floor(eyes[1][3]));
        eyeObjs.right = {
            patch: rightImageData,
            imagex: eyes[1][0]+offsetX,
            imagey: eyes[1][1]+offsetY,
            width: eyes[1][2],
            height: eyes[1][3]        
        };
      
        if (leftImageData.width == 0 || rightImageData.width == 0) {
            console.log('an eye patch had zero width');
            return null;
        }

        return eyeObjs;
    }

    /**
     * Performs eye detection on the passed working image
     * @param {ImageData} workingImage - either the whole canvas or the upper half of the head
     * @param {number} width - width of working image
     * @param {number} height - height of working image
     * @return{array} eyes - array of rectangle information. 
     */
    TrackingjsGaze.prototype.detectEyes = function(workingImage, width, height){         
        var eyes = [];
        var intermediateEyes = [];
        var pixels = workingImage.data;
        tracking.ViolaJones.detect(pixels, width, height, 0.5, 2, 1.7, 0.1, tracking.ViolaJones.classifiers['eye']).forEach(function(rect){
                var intermediateEye = [rect.x, rect.y, rect.width, rect.height];
                intermediateEyes.push(intermediateEye);
        });
        if (intermediateEyes.length>1){
            //find the two eyes with the shortest y distance
            var minimumYDistance = 1000;
            var eyes = [];

            for(var i=0; i < intermediateEyes.length; i++){
                for(var j = i+1; j < intermediateEyes.length; j++){
                    var YDistance = Math.abs(Math.floor(intermediateEyes[i][1]) - Math.floor(intermediateEyes[j][1]));
                    if(YDistance <= minimumYDistance){
                        minimumYDistance = YDistance;
                        eyes[0] = intermediateEyes[i];
                        eyes[1] = intermediateEyes[j];
                    }                       
                }
            }

            eyes.sort(function(a,b) {
              return a[0]-b[0]
            });
            return eyes;
        }
        else{
            console.log('tracking.js could not detect two eyes in the video');
            return null;
        }
    }

    /**
     * Performs face detection on the passed canvas
     * @param {ImageData} workingImage - whole video canvas
     * @param {number} width - width of imageCanvas
     * @param {number} height - height of imageCanvas
     * @return{array} face - array of rectangle information
     */
    TrackingjsGaze.prototype.detectFace = function(workingImage, width, height){
        var intermediateFaces = [];
        var face = [];

        // Detect faces in the image
        var pixels = workingImage.data;
        tracking.ViolaJones.detect(pixels, width, height, 2, 1.25, 2, 0.1, tracking.ViolaJones.classifiers['face']).forEach(function(rect){
                var intermediateFace = [rect.x, rect.y, rect.width, rect.height];
                intermediateFaces.push(intermediateFace);
        });
        face = this.findLargestRectangle(intermediateFaces);
        return face;
    }

    /**
     * Goes through an array of rectangles and returns the one with the largest area
     * @param {array} rectangles - array of format [xCoordinate, yCoordinate, width, height]
     * @return{array} largestRectangle  [xCoordinate, yCoordinate, width, height]
     */
    TrackingjsGaze.prototype.findLargestRectangle = function(rectangles){
        var largestArea = 0;
        var area = 0;
        var largestRectangle = [];
        for (var i = 0; i < rectangles.length; ++i){
            area = rectangles[i][2] * rectangles[i][3];
            if (area > largestArea){
                largestArea = area;
                largestRectangle = rectangles[i];
            }
        }
        return largestRectangle;
    }

    TrackingjsGaze.prototype.name = 'trackingjs';

    return TrackingjsGaze;
});