Display Images in Black & White Using the HTML5 Canvas

By Rob Gravelle

The HTML5 canvas is a bitmap element for rendering graphics, drawings, and images on the fly using JavaScript. The canvas 2D API contains an arsenal of functions that give you the ability to draw pretty much anything you like on the canvas. But then the problem is, how much do you know about graphics and bitmap manipulation. Heck I did some graphic art for years in my early days of Web design and used Paintshop Pro and Photoshop, but I don't know the algorithms behind all those cool effects! Well if you're in the same boat as me, then this is the tutorial for you because today, we're going to get the spiel on how to render a color image in grayscale (black & white) in JavaScript. There's something to be said about black & white photography. It can capture images in a way that color simply can't. If you want to display images in black and white, but don't want to mess with your original color photos, then this is the ideal way to go about it.

Preparing the Canvas

When ever you use HTML5 elements in your pages, it's a good idea to test for browser support, because older browsers - Internet Explorer 8 and earlier for instance - do not support the canvas element. You can easily test for browser support by a) using Modernizr or b) by dynamically creating a canvas using document.createElement() and then testing for the presence of a canvas method such as getContext().

Adding the canvas to the page is as easy as including the <canvas> tag where you want it. You can set its width and height to denote how much space it takes up in the page, but it won't make the canvas any more discernable than a blank page on a white wall! I added a blue border around the canvas in order to show how much real estate it takes up on the page. It's important to make the canvas large enough to accommodate the full image or graphic because otherwise, you'll get clipping.

To draw and manipulate images and graphics on the canvas, we have to use the canvas Context API, retrievable via the Element.getContext() function. It has one parameter, which is the context type we want. The one that we'll be using is "2d". There is also a WebGL 3D context, which is currently only supported by Firefox 4 and Google Chrome 9.

The context object has many functions, one of which is called drawImage(). The drawImage() method allows you to insert other images and even other canvas elements into your canvas context. Its signature can get fairly lengthy as the function can receive either three, five or nine arguments! For our purposes here, three arguments will do just fine. The first argument points to the image object to be included, and the other two specify the X and Y coordinates of the image's top left corner within the canvas.

Now you can't just load up the image any old how. There is a accepted process to follow: create a new Image(), call drawImage() in the image's onload() event, and set the image src property to the image path. Following this procedure assures that the image has been fully loaded before drawing it on the canvas:

<!DOCTYPE HTML>
<html>
<head>
<title>HTML5 Canvas Effects Demo</title>
<script type="text/javascript"> 
  window.onload = function(){
      //try and create a canvas element
      var testCanvas  = document.createElement('canvas');
      
      //check if object supports getContext() method 
      if (testCanvas.getContext !== undefined) {
        var canvas  = document.getElementById('effectsCanvas');
        var context = canvas.getContext('2d');
        //set these to position the image within the canvas
        var destX   = 0;
        var destY   = 0;
       
        //comment out the following line to remove the border
        canvas.style.border = "2px solid blue";  
 
        var imageObj = new Image();
        imageObj.onload = function(){
            context.drawImage(imageObj, destX, destY);
        };
        imageObj.src = 'Rob_at_Greenfields.jpg';
      }
      else {
        document.writeln('You are plum outta luck, cuz your browser does not support the canvas element.');
      }
  };
</script>
</head>
<body>
<h1>HTML5 Canvas Effects Demo</h1>
<canvas id="effectsCanvas" width="500" height="550" > </canvas> 
</body>
</html>

The Luminance Algorithm

There are many algorithms to convert a color image into a grayscale/monochrome one. The one that we are using calculates the luminance of a color as it's perceived by the human eye, so the green channel has more importance that the red and the blue. Our Javascript code loops through every pixel of the image and applies the following luminance formula to it to calculate the grayscale number:

red x 0.3 + green x 0.59 + blue x 0.11

Before we can manipulate the image, we need to fetch all of its pixels using the Context.getImageData() method. It accepts four coordinate arguments for specifying the exact area we want: left, top, width, and height. We can send it the entire canvas area because only pixels which contain part of the image will be affected.

We store the pixels in the imgData variable, which we can then convert into a data array via its data property. A for loop iterates through each pixel, where each color value of a single pixel is set using the luminance formula. Every pixel has four values: red, green, blue, and alpha, but only the colors need to be modified for the grayscale effect

Once we've applied the luminance formula to every pixel in the image, we have to redraw it. We can't use drawImage() this time because we need a function that can accept an ImageData object as an argument. That function is Context.putImageData():

  function grayScale(context, canvas) {
    var imgData = context.getImageData(0, 0, canvas.width, canvas.height);
        var pixels  = imgData.data;
        for (var i = 0, n = pixels.length; i < n; i += 4) {
        var grayscale = pixels[i] * .3 + pixels[i+1] * .59 + pixels[i+2] * .11;
        pixels[i  ] = grayscale;        // red
        pixels[i+1] = grayscale;        // green
        pixels[i+2] = grayscale;        // blue
        //pixels[i+3]              is alpha
    }
    //redraw the image in black & white
    context.putImageData(imgData, 0, 0);
  }
  //add the function call in the imageObj.onload
  imageObj.onload = function(){
      context.drawImage(imageObj, destX, destY);
      grayScale(context, canvas);
  };
  ...

Here is the image I used. Download it to the same folder as the demo file or feel free to substitute your own.

 

Conclusion

Today we explored the luminance formula, one of the many algorithms for converting a color image to grayscale one. To learn more about grayscale algorithms, take a look at this article that describes no less than seven grayscale conversion algorithms! They are incorporated into a VB6 project that you can try out, or you can put the same algorithms to use in our JS grayScale() function. Either way, try them out and have some fun!


If you enjoyed this article, please contribute to Rob's less lucrative music career by purchasing one of Rob's cover or original songs from iTunes.com for only 0.99 cents each.

Rob Gravelle resides in Ottawa, Canada, and is the founder of GravelleConsulting.com. Rob has built systems for Intelligence-related organizations such as Canada Border Services, CSIS as well as for numerous commercial businesses. Email Rob to receive a free estimate on your software project. Should you hire Rob and his firm, you'll receive 15% off for mentioning that you heard about it here!

 

In his spare time, Rob has become an accomplished guitar player, and has released several CDs. His former band, Ivory Knight, was rated as one Canada's top hard rock and metal groups by Brave Words magazine (issue #92).

Rob uses and recommends MochaHost, which provides Web Hosting at $3.10 per month, 2 LifeTime Free Domains, and 6 Months Free!

 



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •