Using Canvas To Turn Image Into Grayscale

This example only works on browsers that supports HTML 5 Canvas such as Firefox 2. If your browsers supports it, you should see a color image of a dog and a grayscale version of the image on the space above. If you only see the color image, then your browser does not support it.

The current version of Opera (version 9.26) support Canvas partially, but not getImageData() and putImageData() used in this example.
See: http://www.opera.com/docs/specs/canvas/ for current Opera support for Canvas.

Internet Explorer does not support canvas as of version 7.

I did not write any workaround for browsers that do not work because I expect as the standard become more widely accepted, more browsers will supoort it fully.

Introduction

HTML 5 introduces Canvas object which can be used to draw and manipulate images. In this example, I used a Canvas to turn an image into grayscale. If your browser supports Canvas, you should see two images above: the normal image and the grayscale one. The grayscale one is not a separate image, it is generated by utilizing Canvas.

Example Code

Here's the JavaScript code

function grayscale(image, bPlaceImage)
{
  var myCanvas=document.createElement("canvas");
  var myCanvasContext=myCanvas.getContext("2d");

  var imgWidth=image.width;
  var imgHeight=image.height;
  // You'll get some string error if you fail to specify the dimensions
  myCanvas.width= imgWidth;
  myCanvas.height=imgHeight;
  //  alert(imgWidth);
  myCanvasContext.drawImage(image,0,0);

  // This function cannot be called if the image is not rom the same domain.
  // You'll get security error if you do.
  var imageData=myCanvasContext.getImageData(0,0, imgWidth, imgHeight);

  // This loop gets every pixels on the image and
    for (j=0; j<imageData.height; i++)
    {
      for (i=0; i<imageData.width; j++)
      {
         var index=(i*4)*imageData.width+(j*4);
         var red=imageData.data[index];
         var green=imageData.data[index+1];
         var blue=imageData.data[index+2];
         var alpha=imageData.data[index+3];
         var average=(red+green+blue)/3;
   	    imageData.data[index]=average;
         imageData.data[index+1]=average;
         imageData.data[index+2]=average;
         imageData.data[index+3]=alpha;
       }
     }

    if (bPlaceImage)
	{
	  var myDiv=document.createElement("div");
	     myDiv.appendChild(myCanvas);
	  image.parentNode.appendChild(myCanvas);
	}
	return myCanvas.toDataURL();
  }

 

The function takes two parameters: an image (from <img> element), which must have been loaded. In this example, I made sure that the image has been loaded by attaching the function call to onload. The other parameter is a flag I added to indicate whether the image should be placed on the html page next to the original image.

The function returns an Image object (you can see this being used on the Using Canvas for Mouse Over example below).

<img id="myImage" src="image.gif"
   onload="javascript:grayscale(this, true);"></img>

Inside the function, create a canvas element:

var myCanvas=document.createElement("canvas");

Get the context of the canvas (to draw in the canvas, we need to do work on a canvas context).

var myCanvasContext=myCanvas.getContext("2d");

Then we set the dimensions to be the same as the image

var imgWidth=image.width;
var imgHeight=image.height;
// You'll get some string error if you fail to specify the dimensions
myCanvas.width= imgWidth;
myCanvas.height=imgHeight;

Because I want to manipulate the image, I need to be able to access the pixel data to retrieve the pixel colors, so we need to draw the image into the canvas first to get a duplicate of the original.

myCanvasContext.drawImage(image,0,0);

Call getImageData to retrieve the ImageData object. (this object will have the pixel data)

// This function cannot be called if the image is not from the same domain.
// You'll get security error

var imageData=myCanvasContext.getImageData(0,0,
  imgWidth-1, imgHeight-1);

Change every pixel to level of gray. This simple algorithm averages the red, green, and blue components of every pixels. Note that I use ImageData.width and ImageData.height, not the Image.width and Image.height as they might be aligned differently (in our case, it doesn't really matter because the extra pixels are not going to show up in the final result).

for (j=0; j<imageData.height; i++)
{
  for (i=0; i<imageData.width; j++)
  {
    var index=(i*4)*imageData.width+(j*4);

    var red=imageData.data[index];
    var green=imageData.data[index+1];
    var blue=imageData.data[index+2];
    var alpha=imageData.data[index+3];

    var average=(red+green+blue)/3;

    imageData.data[index]=average;
    imageData.data[index+1]=average;
    imageData.data[index+2]=average;
    imageData.data[index+3]=alpha;
  }
}

Every pixel is represented by 4 numbers in the format of red, green, blue, and alpha. Each color is a number between 0 to 255. In the code, you can see where I retrieved the red, green, blue, and alpha in the portion below:

    var red=imageData.data[index];
    var green=imageData.data[index+1];
    var blue=imageData.data[index+2];
    var alpha=imageData.data[index+3];

    var average=(red+green+blue)/3;

Then I averaged them. (To read about color, see INTERPRETING THE NUMBERS section in this other tutorial - disregard the discussion about web-safe color.)

The alpha is not used in my example and there really is no need for the last line below, but you could turn some color transparent by changing this alpha value to something between 0 to 255 where 0 means fully invisible and 255 means fully visible).

    imageData.data[index]=average;
    imageData.data[index+1]=average;
    imageData.data[index+2]=average;
    imageData.data[index+3]=alpha;

Puting back back the modified image into the image:

myCanvasContext.putImageData(imageData,0,0,0,0, imageData.width,   imageData.height);

The rest of the code reates a new <div> element to insert the new canvas (which has the new gray-scale version of the image) into it. This part of the code is not really part of the subject and will only be executed if bPlaceImage is true. I added it for demonstration purpose.

var myDiv=document.createElement("div");
myDiv.appendChild(myCanvas);
image.parentNode.appendChild(myCanvas);

The return value of the function is an Image.src object which can be used to replace existing image (see the next example for its use).

return myCanvas.toDataURL();

 
Using Canvas for Mouse Over

In this example, we use the image generated by the grayscale() function to create mouse over effect. Mouse over the image below, it will change into grayscale (assuming your browser supports the feature).

When the image is loaded, it calls prepareMouseOverImage() function (discussed later) which creates the grayscale version of the image and assign onmouseover and onmouseout event handlers.

<img id="myImage" src="images/volcano.jpg"   onload="javascript:prepareMouseOverImage(this, 'images/volcano.jpg');">
</img>

Below is the prepareMouseOverImage() function which is made a bit complicated because it needs to save the original image. This is done by saving the URL (you'd think saving image.src will work, but it didn't).

onmouseover and onmouseout event handlers are assigned to swap the image.

function prepareMouseOverImage(image, originalURL)
{

  // Save the original Image URL
  image.mouseOverImage=originalURL;

  // Create the grayscale image
  image.normalImage=grayscale(image, false);


  // Override the old handler (otherwise the onmouse over event handler will

  // call it again).
  image.onload=function(){return true;};



  // Assign onmouseover and onmouseout event handlers
  image.onmouseover=function()
  {
    this.src=this.mouseOverImage;
  }

  image.onmouseout=function()
  {
    this.src=this.normalImage;
  }


  // Set the grayscale as the "normal" state image
  image.src=image.normalImage;
}

Imagine the convenience of this method to creating mouse over and other image effects.

Another example: Using Canvas To Allow User To Flip Images

Another example: Using Canvas To Allow User To Draw On Your Web Page

Another tutorial on how to save Canvas into files.

For more information about Canvas, check out:
http://www.whatwg.org/specs/web-apps/current-work/multipage/section-the-canvas.html#canvas