define('pupil', ['util'], function(util) {
/**
* Returns intensity value at x,y position of a pixels image
* @param{array} pixels - array of size width*height
* @param{number} x - input x value
* @param{number} y - input y value
* @param{number} width - width of pixels image
* @returns{number} - intensity value in [0,255]
*/
var getValue = function (pixels, x, y, width){
return pixels[y * width + x];
}
/**
* Contains methods which detect the center of an eye's pupil
* @alias module:pupil
* @exports pupil
*/
var pupil = {};
/**
* Computes summation area table/integral image of a pixel matrix
* @param{array} pixels value of eye area
* @param{number} width - of image in 'pixels'
* @param{number} height - of image in 'pixels'
* @returns{number[][]} -integral image
*/
var getSumTable = function (pixels, width, height){
var integralImage = new Array(width);
var sumx = 0;
var sumy = 0;
for (var i = 0; i < width; i++){
integralImage[i] = new Array(height);
sumx += getValue(pixels, i, 0, width);
integralImage[i][0] = sumx;
}
for (var i = 0; i < height; i++){
sumy += getValue(pixels, 0, i, width);
integralImage[0][i] = sumy;
}
for (var x = 1; x < width; x++){
for (var y = 1; y < height; y++){
integralImage[x][y] = getValue(pixels, x, y, width) + integralImage[x - 1][y] + integralImage[x][y - 1] - integralImage[x - 1][y - 1];
}
}
return integralImage;
}
/**
* Detects a pupil in a set of pixels
* @param {array} pixels - patch of pixels to look for pupil into
* @param {number} width - of pixel patch
* @param {number} height - of pixel patch
* @return {array} coordinate of the bottom right corner and width of the best fitted pupil
*/
var getSinglePupil = function (pixels, width, height){
var summedAreaTable = getSumTable(pixels, width, height);
var bestAvgScore = 999999; //want to minimize this score
var bestPoint = [0, 0]; //bottom right corner of best fitted pupil
var bestHalfWidth = 0; //corresponding half width of the best fitted pupil
var offset = Math.floor(width / 10.0); //padding
//halfWidth could also start at 1, but this makes it faster
for (var halfWidth = Math.floor(height / 10.0); halfWidth < width / 2; halfWidth++){
//think of a sliding rectangular window of width halfWidth*2 that goes through the whole eye pixel matrix and does the following:
//1) computes the irisArea, which is the total intensity of the iris
//2) computes the scleraIrisArea, which is multiple rows of pixels including the sclera and iris.
//3) computes avg, which is the intensity of the area divided by the number of pixels.
//start at the bottom right of the rectangle!not top left
for (var x = halfWidth; x < width - offset; x++){
for (var y = halfWidth; y < height - offset; y++){
//evaluate area by the formula found on wikipedia about the summed area table: I(D)+I(A)-I(B)-I(C)
var irisArea = summedAreaTable[x + offset][y + offset] + summedAreaTable[x + offset - halfWidth][y + offset - halfWidth] - summedAreaTable[x + offset][y + offset - halfWidth] - summedAreaTable[x + offset - halfWidth][y + offset];
var avgScore = 1.0 * irisArea / ((halfWidth + 1) * (halfWidth + 1)) + 1;
//summation area table again
var scleraIrisArea = ((1.0 * summedAreaTable[width - 1 - offset][y + offset] + summedAreaTable[0 + offset][y + offset - halfWidth] - summedAreaTable[0 + offset][y + offset] - summedAreaTable[width - 1 - offset][y + offset - halfWidth]) - irisArea);
//minimize avgScore/scleraIrisArea. 150 is too high, might have to change since it's closer to white
if ((avgScore) / scleraIrisArea < bestAvgScore && avgScore < 150){
bestAvgScore = (avgScore) / scleraIrisArea;
bestPoint = [x + offset, y + offset];
bestHalfWidth = halfWidth;
}
}
}
}
return [bestPoint, bestHalfWidth];
}
/**
* Given an Object with two eye patches it finds the location of the detected pupils
* @param {Object} eyesObj - left and right detected eye patches
* @return {Object} eyesObj - updated eye patches with information about pupils' locations
*/
pupil.getPupils = function(eyesObj) {
if (!eyesObj) {
return eyesObj;
}
if (!eyesObj.left.blink) {
eyesObj.left.pupil = getSinglePupil(Array.prototype.slice.call(util.grayscale(eyesObj.left.patch, eyesObj.left.width, eyesObj.left.height)), eyesObj.left.width, eyesObj.left.height);
eyesObj.left.pupil[0][0] -= eyesObj.left.pupil[1];
eyesObj.left.pupil[0][1] -= eyesObj.left.pupil[1];
}
if (!eyesObj.right.blink) {
eyesObj.right.pupil = getSinglePupil(Array.prototype.slice.call(util.grayscale(eyesObj.right.patch, eyesObj.right.width, eyesObj.right.height)), eyesObj.right.width, eyesObj.right.height);
eyesObj.right.pupil[0][0] -= eyesObj.right.pupil[1];
eyesObj.right.pupil[0][1] -= eyesObj.right.pupil[1];
}
return eyesObj;
}
return pupil;
});