| Home | Image Processing Tool | Technical Details | Browser Compatibility | Source Code |
![]()
Copyright 2012 by András Péter
E-Mail: info@dog-vision.com
The JavaScript file doing the image processing can be viewn below and downloaded from here.
// Copyright 2012 by András Péter
// Licensed under the GNU General Public License v3: http://www.gnu.org/licenses/gpl.html
function imgprocess(
// Load parameters into variables
// Image data from a canvas element's getImageData function
pixels,
// Apply deuteranopia effect? (boolean)
deuter,
// Apply "decreased brightness discrimination" effect? (boolean)
decbright,
// Factor by which to reduce visual acuity (boolean)
decac
) {
// Prepare variables necessary for processing the image
var
// Width of image
imgWidth = pixels.width,
// Height of image
imgHeight = pixels.height,
// Array for storing original pixel data
data = new Array((imgWidth * imgHeight) << 2),
// Array for storing modified pixel data
tdata = new Array((imgWidth * imgHeight) << 2),
// Ratio to modify amount of red light perceived relative to green
rm = 0.4 / 1.4,
// Ratio to modify amount of green light perceived relative to red
gm = 1 / 1.4,
// Loop counter
r = 0,
// Variables for storing color values temporarily
redtemp = greentemp = bluetemp = 0,
// Variable for storing overall average brightness and corrected brightness
bright = 0,
// Value used for gamma compression
gc = 1 / 2.2,
// Value used for dividing by two (because multiplication is faster)
tworec = 1 / 2,
// Counter variables for marking upper and lower boundry of blurring window
brl = brhh = 0,
// Four times the width of the image, used to step counter variables at vertical pass
imgWF = imgWidth * 4;
// If the number of perceived colors should be decreased...
if (deuter) {
// Do gamma expansion and move pixel data into the array for storing modified pixel data...
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
// ...at the same time sum the values of each channel for calculating average brightness
redtemp += tdata[r] = Math.pow(pixels.data[r], 2.2);r++;
greentemp += tdata[r] = Math.pow(pixels.data[r], 2.2);r++;
bluetemp += tdata[r] = Math.pow(pixels.data[r], 2.2);r -= 2;
// Merge red and green channels using their respective weights
tdata[r + 1] = tdata[r] * rm + tdata[r + 1] * gm;
r += 4;
}
}
// Calculate average brightness for each channel
redtemp = redtemp / (imgWidth * imgHeight);
greentemp = greentemp / (imgWidth * imgHeight);
bluetemp = bluetemp / (imgWidth * imgHeight);
// Calculate brightness value that will be used to compensate for the brightness change that results from decreasing the number of colors
bright = redtemp * 0.30 + greentemp * 0.59 - ((redtemp * rm + greentemp * gm) * 0.89);
// If the contrast should be decreased double the value and add the overall average brightness
if (decbright) {bright += bright + (redtemp * 0.30 + greentemp * 0.59 + bluetemp * 0.11)}
// Start incremental bilinear blur:
// -decac is the factor by which to reduce visual acuity
// -brad is the half width/height of the averaging window at the actual pass
for (var brad = 1; brad < decac; brad++) {
var
// Value for dividing summed pixel values of the averaging window
bradvtrec = 1 / (brad * 4),
// Image width decreased by half of the averaging window's width (to avoid border problems)
imgWmb = imgWidth - brad,
// Image height decreased by half of the averaging window's height (to avoid border problems)
imgHmb = imgHeight - brad;
// Move modified pixel data to array storing original pixel data
r = 1;
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
data[r] = tdata[r];r++;
data[r] = tdata[r];r += 3;
}
}
// Do vertical pass
for (var i = 0; i < imgWidth; i++) {
// Set upper and lower bound of averaging window
brl = brhh = (i << 2) + 1;
// Fill temporary variables with the pixel value of the upper border
greentemp = data[brl] * (brad + brad + 1);
bluetemp = data[brl + 1] * (brad + brad + 1);
// Fill the rest of the temporary variables with the pixel values near the border
for (var bj = 1; bj < brad; bj++) {
if (bj < imgHeight-1) {brhh += imgWF}
greentemp += data[brhh] + data[brhh];
bluetemp += data[brhh + 1] + data[brhh + 1];
}
if (brad < imgHeight-1) {brhh += imgWF}
greentemp += data[brhh];
bluetemp += data[brhh + 1];
// Produce the average of the averaging window and store it in the array for modified pixel values
r = (i << 2) + 1;
tdata[r] = greentemp * bradvtrec;
tdata[r + 1] = bluetemp * bradvtrec;
// Do vertical pass at the current column
for (var j = 1; j < imgHeight; j++) {
// Remove uppermost pixel values from the temporary variables
greentemp -= data[brl];
bluetemp -= data[brl + 1];
// Increase variable containing upper bound of averaging window
if (j > brad) {brl += imgWF}
// Remove uppermost pixel values from the temporary variables
greentemp -= data[brl];
bluetemp -= data[brl + 1];
// Add the pixel values at the lowest position of the averaging window to the temporary variables
greentemp += data[brhh];
bluetemp += data[brhh + 1];
// Increase variable containing lower bound of averaging window
if (j < imgHmb) {brhh += imgWF}
// Add the pixel values at the lowest position of the averaging window to the temporary variables
greentemp += data[brhh];
bluetemp += data[brhh + 1];
// Produce the average of the averaging window and store it in the array for modified pixel values
r += imgWF;
tdata[r] = greentemp * bradvtrec;
tdata[r + 1] = bluetemp * bradvtrec;
}
}
// Move modified pixel data to array storing original pixel data
r = 1;
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
data[r] = tdata[r];r++;
data[r] = tdata[r];r += 3;
}
}
// Do horizontal pass
for (var j = 0; j < imgHeight; j++) {
// Set left and right bound of averaging window
brl = brhh = ((j * imgWidth) << 2) + 1;
// Fill temporary variables with the pixel value of the left border
greentemp = data[brl] * (brad + brad + 1);
bluetemp = data[brl + 1] * (brad + brad + 1);
// Fill the rest of the temporary variables with the pixel values near the border
for (var bi = 1; bi < brad; bi++) {
if (bi < imgWidth-1) {brhh += 4}
greentemp += data[brhh] + data[brhh];
bluetemp += data[brhh + 1] + data[brhh + 1];
}
if (brad < imgWidth-1) {brhh += 4}
greentemp += data[brhh];
bluetemp += data[brhh + 1];
// Produce the average of the averaging window and store it in the array for modified pixel values
r = ((j * imgWidth) << 2) + 1;
tdata[r] = greentemp * bradvtrec;
tdata[r + 1] = bluetemp * bradvtrec;
// Do horizontal pass at the current row
for (var i = 1; i < imgWidth; i++) {
// Remove leftmost pixel values from the temporary variables
greentemp -= data[brl];
bluetemp -= data[brl + 1];
// Increase variable containing left bound of averaging window
if (i > brad) {brl += 4}
// Remove leftmost pixel values from the temporary variables
greentemp -= data[brl];
bluetemp -= data[brl + 1];
// Add the pixel values at the rightmost position of the averaging window to the temporary variables
greentemp += data[brhh];
bluetemp += data[brhh + 1];
// Increase variable containing right bound of averaging window
if (i < imgWmb) {brhh += 4}
// Add the pixel values at the rightmost position of the averaging window to the temporary variables
greentemp += data[brhh];
bluetemp += data[brhh + 1];
// Produce the average of the averaging window and store it in the array for modified pixel values
r += 4;
tdata[r] = greentemp * bradvtrec;
tdata[r + 1] = bluetemp * bradvtrec;
}
}
}
// If the contrast should be decreased...
if (decbright) {
r = 0;
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
// Half the pixel values, compensate for brightness loss and do gamma compression
pixels.data[r + 1] = Math.pow((tdata[r + 1] + bright) * tworec, gc);
pixels.data[r + 2] = Math.pow((tdata[r + 2] + bright) * tworec, gc);
// Red and green pixel values are the same
pixels.data[r] = pixels.data[r + 1];
r += 4;
}
}
}
// If the contrast should not be decreased...
else {
r = 0;
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
// Compensate for brightness loss and do gamma compression
pixels.data[r + 1] = Math.pow(tdata[r + 1] + bright, gc);
pixels.data[r + 2] = Math.pow(tdata[r + 2] + bright, gc);
// Red and green pixel values are the same
pixels.data[r] = pixels.data[r + 1];
r += 4;
}
}
}
}
// If the number of perceived colors should not be decreased...
else {
// If the contrast should be decreased...
if (decbright) {
// Do gamma expansion and move pixel data into the array for storing modified pixel data...
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
// ...at the same time sum the values of each channel for calculating average brightness
redtemp += tdata[r] = Math.pow(pixels.data[r], 2.2);r++;
greentemp += tdata[r] = Math.pow(pixels.data[r], 2.2);r++;
bluetemp += tdata[r] = Math.pow(pixels.data[r], 2.2);r += 2;
}
}
// Calculate average brightness for each channel
redtemp = redtemp / (imgWidth * imgHeight);
greentemp = greentemp / (imgWidth * imgHeight);
bluetemp = bluetemp / (imgWidth * imgHeight);
// Calculate the overall average brightness
bright = (redtemp * 0.30 + greentemp * 0.59 + bluetemp * 0.11);
}
// If the contrast should not be decreased...
else {
// Do gamma expansion and move pixel data into the array for storing modified pixel data
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
tdata[r] = Math.pow(pixels.data[r], 2.2);r++;
tdata[r] = Math.pow(pixels.data[r], 2.2);r++;
tdata[r] = Math.pow(pixels.data[r], 2.2);r += 2;
}
}
}
// Start incremental bilinear blur:
// -decac is the factor by which to reduce visual acuity
// -brad is the half width/height of the averaging window at the actual pass
for (var brad = 1; brad < decac; brad++) {
var
// Value for dividing summed pixel values of the averaging window
bradvtrec = 1 / (brad * 4),
// Image width decreased by half of the averaging window's width (to avoid border problems)
imgWmb = imgWidth - brad,
// Image height decreased by half of the averaging window's height (to avoid border problems)
imgHmb = imgHeight - brad;
// Move modified pixel data to array storing original pixel data
r = 0;
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
data[r] = tdata[r];r++;
data[r] = tdata[r];r++;
data[r] = tdata[r];r += 2;
}
}
// Do vertical pass
for (var i = 0; i < imgWidth; i++) {
// Set upper and lower bound of averaging window
brl = brhh = (i << 2);
// Fill temporary variables with the pixel value of the upper border
redtemp = data[brl] * (brad + brad + 1);
greentemp = data[brl + 1] * (brad + brad + 1);
bluetemp = data[brl + 2] * (brad + brad + 1);
// Fill the rest of the temporary variables with the pixel values near the border
for (var bj = 1; bj < brad; bj++) {
if (bj < imgHeight-1) {brhh += imgWF}
redtemp += data[brhh] + data[brhh];
greentemp += data[brhh + 1] + data[brhh + 1];
bluetemp += data[brhh + 2] + data[brhh + 2];
}
if (brad < imgHeight-1) {brhh += imgWF}
redtemp += data[brhh];
greentemp += data[brhh + 1];
bluetemp += data[brhh + 2];
// Produce the average of the averaging window and store it in the array for modified pixel values
r = (i << 2);
tdata[r] = redtemp * bradvtrec;
tdata[r + 1] = greentemp * bradvtrec;
tdata[r + 2] = bluetemp * bradvtrec;
// Do vertical pass at the current column
for (var j = 1; j < imgHeight; j++) {
// Remove uppermost pixel values from the temporary variables
redtemp -= data[brl];
greentemp -= data[brl + 1];
bluetemp -= data[brl + 2];
// Increase variable containing upper bound of averaging window
if (j > brad) {brl += imgWF}
// Remove uppermost pixel values from the temporary variables
redtemp -= data[brl];
greentemp -= data[brl + 1];
bluetemp -= data[brl + 2];
// Add the pixel values at the lowest position of the averaging window to the temporary variables
redtemp += data[brhh];
greentemp += data[brhh + 1];
bluetemp += data[brhh + 2];
// Increase variable containing lower bound of averaging window
if (j < imgHmb) {brhh += imgWF}
// Add the pixel values at the lowest position of the averaging window to the temporary variables
redtemp += data[brhh];
greentemp += data[brhh + 1];
bluetemp += data[brhh + 2];
// Produce the average of the averaging window and store it in the array for modified pixel values
r += imgWF;
tdata[r] = redtemp * bradvtrec;
tdata[r + 1] = greentemp * bradvtrec;
tdata[r + 2] = bluetemp * bradvtrec;
}
}
// Move modified pixel data to array storing original pixel data
r = 0;
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
data[r] = tdata[r];r++;
data[r] = tdata[r];r++;
data[r] = tdata[r];r += 2;
}
}
// Do horizontal pass
for (var j = 0; j < imgHeight; j++) {
// Set left and right bound of averaging window
brl = brhh = ((j * imgWidth) << 2);
// Fill temporary variables with the pixel value of the left border
redtemp = data[brl] * (brad + brad + 1);
greentemp = data[brl + 1] * (brad + brad + 1);
bluetemp = data[brl + 2] * (brad + brad + 1);
// Fill the rest of the temporary variables with the pixel values near the border
for (var bi = 1; bi < brad; bi++) {
if (bi < imgWidth-1) {brhh += 4}
redtemp += data[brhh] + data[brhh];
greentemp += data[brhh + 1] + data[brhh + 1];
bluetemp += data[brhh + 2] + data[brhh + 2];
}
if (brad < imgWidth-1) {brhh += 4}
redtemp += data[brhh];
greentemp += data[brhh + 1];
bluetemp += data[brhh + 2];
// Produce the average of the averaging window and store it in the array for modified pixel values
r = ((j * imgWidth) << 2);
tdata[r] = redtemp * bradvtrec;
tdata[r + 1] = greentemp * bradvtrec;
tdata[r + 2] = bluetemp * bradvtrec;
// Do horizontal pass at the current row
for (var i = 1; i < imgWidth; i++) {
// Remove leftmost pixel values from the temporary variables
redtemp -= data[brl];
greentemp -= data[brl + 1];
bluetemp -= data[brl + 2];
// Increase variable containing left bound of averaging window
if (i > brad) {brl += 4}
// Remove leftmost pixel values from the temporary variables
redtemp -= data[brl];
greentemp -= data[brl + 1];
bluetemp -= data[brl + 2];
// Add the pixel values at the rightmost position of the averaging window to the temporary variables
redtemp += data[brhh];
greentemp += data[brhh + 1];
bluetemp += data[brhh + 2];
// Increase variable containing right bound of averaging window
if (i < imgWmb) {brhh += 4}
// Add the pixel values at the rightmost position of the averaging window to the temporary variables
redtemp += data[brhh];
greentemp += data[brhh + 1];
bluetemp += data[brhh + 2];
// Produce the average of the averaging window and store it in the array for modified pixel values
r += 4;
tdata[r] = redtemp * bradvtrec;
tdata[r + 1] = greentemp * bradvtrec;
tdata[r + 2] = bluetemp * bradvtrec;
}
}
}
// If the contrast should be decreased...
if (decbright) {
r = 0;
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
// Half the pixel values, compensate for brightness loss and do gamma compression
pixels.data[r] = Math.pow((tdata[r] + bright) * tworec, gc);r++;
pixels.data[r] = Math.pow((tdata[r] + bright) * tworec, gc);r++;
pixels.data[r] = Math.pow((tdata[r] + bright) * tworec, gc);r += 2;
}
}
}
// If the contrast should not be decreased...
else {
r = 0;
for (var i = 0; i < imgWidth; i++) {
for (var j = 0; j < imgHeight; j++) {
// Do gamma compression
pixels.data[r] = Math.pow(tdata[r], gc);r++;
pixels.data[r] = Math.pow(tdata[r], gc);r++;
pixels.data[r] = Math.pow(tdata[r], gc);r += 2;
}
}
}
}
// Send back pixel data to the calling script
return pixels;
};
![]() |
![]() |