Using Canvas to Turn Color Photos to Black and White

This topic explains how to use HTML5 Canvas to turn a color photograph into a black-and-white one to create artistic or antique effects for web illustrations.

  • Canvas Cross-Domain Security Issues
  • Canvas Code Sample
  • Canvas Code Sample Discussion
  • Body Code
  • Script Code
    • displayImage Function
    • getColorData Function
    • putColorData Function
    • noPhoto Function
    • Code Requirements
  • Related topics

This topic includes a stand-alone annotated code sample that shows how to transform the photo of an American Kestral falcon from multicolor to black and white. The code sample demonstrates how to use Canvas to load the falcon photograph into a webpage and display it on the screen. You will then see how to make your program read the red, green, and blue values of each pixel and calculate the corresponding grayscale values. After the value is calculated, the program changes the red, green, and blue values for each pixel. The discussion material at the end of this sample explains more about how the code works to develop this technique.

Canvas Cross-Domain Security Issues

The Using Canvas to Make Photos into Shapes and the Using Canvas to Add Texture to Photos topics show you how to create photographic special effects by reading and writing pixels to the Canvas. The special effects technique covered in this topic uses the getImageData method to read pixels directly from an image. This method has an important security requirement that does not apply to the first two topics. This security requirement stops a Canvas program from copying pixels from one computer domain to another, and is necessary to prevent Internet forgery.

In order to comply with this security requirement, you can only read pixels from an image that is in the same domain as the code sample in this topic. To run the code sample, you need to place a photo named "kestral.png" in the same folder as the html code you are running. This puts the image in the same domain as the program, making it possible to run the program. If your original image is on another domain and you attempt to read pixels using the getImageData or toDataURL methods, your program might stop responding and cause an error. However, if your image is on a server or in the cloud, and your code is on the same domain as the image, getImageData or toDataURL will reads the pixels without a problem. For more information about these security issues, see the documentation for getImageData and toDataURL.

Canvas Code Sample

<!DOCTYPE html>
<html>
  
  <head>
    <script type="text/javascript">
    
      //Global variables
      var picWidth = 200; // width of the canvas
      var picHeight = 200; // height of the canvas
      var picLength = picWidth * picHeight; // number of chunks
      var myImage = new Image(); // Create a new blank image.
      
      // Load the image and display it.
      function displayImage() {

        // Get the canvas element.
        canvas = document.getElementById("myCanvas");

        // Make sure you got it.
        if (canvas.getContext) {

          // Specify 2d canvas type.
          ctx = canvas.getContext("2d");

          // When the image is loaded, draw it.
          myImage.onload = function() {
            // Load the image into the context.
            ctx.drawImage(myImage, 0, 0);

            // Get and modify the image data.
            getColorData();

            // Put the modified image back on the canvas.
            putColorData();
          }

          // Define the source of the image.
          // This file must be on your machine in the same folder as this web page.
          myImage.src = "kestral.png";
        }
      }

      function getColorData() {

        myImage = ctx.getImageData(0, 0, 200, 200);

        // Loop through data.
        for (var i = 0; i < picLength * 4; i += 4) {

          // First bytes are red bytes.        
          // Get red value.
          var myRed = myImage.data[i];

          // Second bytes are green bytes.
          // Get green value.
          var myGreen = myImage.data[i + 1];

          // Third bytes are blue bytes.
          // Get blue value.
          var myBlue = myImage.data[i + 2];

          // Fourth bytes are alpha bytes
          // We don't care about alpha here.
          // Add the three values and divide by three.
          // Make it an integer.
          myGray = parseInt((myRed + myGreen + myBlue) / 3);

          // Assign average to red, green, and blue.
          myImage.data[i] = myGray;
          myImage.data[i + 1] = myGray;
          myImage.data[i + 2] = myGray;
        }
      }

      function putColorData() {

        ctx.putImageData(myImage, 0, 0);
      }
      
      function noPhoto() {
      
        alert("Please put a .png file in this folder and name it kestral.png.");
        
        }
      
    </script>
  </head>
  
  <body onload="displayImage()">
    <h1>
      American Kestral
    </h1>
    <p>
      The original image is on the left and the modified image is on the right.
    </p>
    <img id="myPhoto" src="kestral.png" onerror="noPhoto()">
    <canvas id="myCanvas" width="200" height="200">
    </canvas>
    <p>
      Public domain image courtesy of U.S. Fish and Wildlife Service.
    </p>
  </body>

</html>

Canvas Code Sample Discussion

This discussion explains the design and structure of this Canvas code sample and how all the parts work together. The sample uses a standard HTML5 header, <!doctype html>, so that browsers can distinguish it as part of the HTML5 specification. This code is divided into two major parts:

  • Body Code
  • Script Code

Body Code

The body tag uses the onload function to call the displayImage function when the page is loaded. An original image of a kestral is loaded into the body so you can compare it to the one that will be modified by Canvas. The onerror event is added to the img element to determine if the image is loaded. The canvas element is part of the body. The initial width and height of the Canvas is specified, as is the id attribute. The ID is required so that the canvas element can be added to the object model of the page.

Script Code

The script code includes four functions: displayImage, getColorData, putColorData, and noPhoto. The displayImage function is called when the page is loaded. The getColorData and putColorData functions are called from displayImage. The noPhoto function is called if there is an error when the photo is loaded. Global variables are created at the beginning of the script section to create an empty image file, which will be used later, as well as variables that define the height and width of the canvas. In addition, the length of the image array is calculated, determining the number of 4-byte values in the array.

displayImage Function

This function is called on page load. It gets the canvas by using the ID of the canvas element in the body code. It then gets the CanvasRenderingContext2D Object of the canvas, making it ready to accept drawing and uses DrawImage to load the image into the context. After the context is initialized as a 2D canvas, the canvas is ready to be drawn to.

The last thing the function does is to specify the source of the image by supplying a path. Because images might not load instantly, an event is set up that will call a function when the image actually gets loaded. After the image is loaded, it is displayed and a call is made to the getColorData and putColorData functions, which complete the remaining drawing work.

Unlike the first two topics in this tutorial, you must place the kestral image on your own computer. You cannot call it from another domain. If the pixels on the canvas are from another domain, the image is considered a security threat, and you cannot modify it. For more information about cross-domain security threats, see the canvas documentation. Any file named "kestral.png" will work. If you want to use the original kestral image, right-click the left image of the kestral in the first topic of this scenario and click Save picture as ... and save it to your machine, placing it in the same folder as your webpage.

getColorData Function

The image is copied with getImageData to myImage and processed by moving through the image data array and gathering the value of the first three bytes (red, green, and blue; alpha is ignored). The image then adds the values of the three bytes and divides the sum by three. The result is made into an integer and written into values of those three bytes. This value corresponds to the hue of the color, but is a grayscale value running from 0 to 255. The result is that the image is a black and white grayscale image that looks like the original color image.

Note  For more information about Canvas pixel colors, see the Changing Photo Colors section.

 

putColorData Function

This function simply writes the changed image data back to the canvas using putImageData. It is often useful to have a second image to do the actual processing and display the result only when the processing is finished.

noPhoto Function

This function is called if the img element is not able to load a file called Kestral.png. An alert appears, telling the reader that the correct file is not loaded.

Code Requirements

This code runs in Windows Internet Explorer 9. It does not work in earlier versions of Windows Internet Explorer, but might run in other browsers that support HTML5 Canvas.

How to Create Canvas Special Effects