- Chapter 9: Using Video with Canvas
- The Canvas Element
- The 2D API
- Taking a Screen Shot of an HTML5 Video
- Making a Copy of a Playing Video
- Playing the Video Copy in Greyscale
- Wrapping Up
Playing the Video Copy in Greyscale
Another useful thing that you can do with objects within the canvas drawing context is to alter the individual pixels. You’ll do just that in this example by altering the pixels in the video so they appear grey rather than in colour.
You’ll be building on the code created in the first example in this chapter. So, the first thing you need to do is to define a second canvas for the manipulations to take place. This is used as a background canvas, which you’ll use to work on before drawing the final result on the actual display canvas. The background canvas will only exist within the JavaScript code, not as an HTML element on the page, and therefore won’t be seen by the user.
To do this, you simply create a new instance of the canvas element using the standard createElement() JavaScript function:
var bgCanvas= document.createElement(‘canvas’);
Once this instance has been created, you also need a handle to its 2D drawing context so you can use it:
var bgContext = bgCanvas.getContext(‘2d’);
In the earlier example, you set the width and height of the display canvas via JavaScript, and you also need to set the background canvas to the same dimensions:
video.addEventListener(‘loadedmetadata’, function() { ratio = video.videoWidth / video.videoHeight; w = video.videoWidth - 100; h = parseInt(w / ratio, 10); canvas.width = w; canvas.height = h; bgCanvas.width = w; bgCanvas.height = h; }, false);
Before you alter the pixels, you need to meet two new functions that you’ll use: getImageData(x, y, w, h) and putImageData(imageData, x, y). The getImageData(x, y, w, h) function returns an ImageData object for the rectangle specified by the parameters: x and y specify the upper-left corner of the rectangle, and w and h are the rectangle’s width and height.
The ImageData object that is returned, as its name suggests, contains the image data for the rectangle specified in the call getImageData(). The information that this returned object contains is listed in Table 9.2.
Table 9.2 ImageData Object Contents
Attribute |
Description |
---|---|
width |
The width of the returned image data rectangle. |
height |
The height of the returned image data rectangle. |
data |
A two-dimensional array that contains the actual pixel information for each pixel. Each pixel has four entries listed sequentially; values for the pixels are red, green, blue, and alpha channel. For example, if the first pixel is white, it would contain the information: data[0][0] = 255 // red channel data[0][1] = 255 // green channel data[0][2] = 255 // blue channel data[0][3] = 1 // alpha channel (opacity) |
As you’ve probably guessed, it will be through the data attribute that you’ll be able to alter the pixel information of the image.
The other function, putImageData(imageData, x, y), paints the pixel data specified in imageData at the coordinates indicated by x and y. Using this information, let’s make the video grey!
Define the function makeItGrey(), which will do all the required shenanigans:
function makeItGrey() { bgContext.drawImage(video, 0, 0, w, h); var pixelData = bgContext.getImageData(0, 0, w, h); for (var i = 0; i < pixelData.data.length; i += 4 ) { var r = pixelData.data[i]; var g = pixelData.data[i+1]; var b = pixelData.data[i+2]; var averageColour = (r + g + b) / 3; pixelData.data[i] = averageColour; pixelData.data[i+1] = averageColour; pixelData.data[i+2] = averageColour; } context.putImageData(pixelData, 0, 0); }
Let’s walk through the code, starting with the background context:
bgContext.drawImage(video, 0, 0, w, h);
Similar to the earlier example, you need to take a snapshot of the video and draw it to the canvas. You do the same here, but this time you draw the image on the background context rather than the display context.
You then obtain the pixel data from the background context’s image that you’ve just drawn. You’ll be manipulating the data returned in the pixelData object:
var pixelData = bgContext.getImageData(0, 0, w, h);
You then need to loop through each item in the pixelDatas data array, which contains the actual information for each pixel. As mentioned in Table 9.2, the data array is a two-dimensional array with four data items for each pixel. It is for this reason that the loop counter is set to add 4 for each iteration:
for (var i = 0; i < pixelData.data.length; i += 4 ) { ... }
For each pixel, you then need to extract the relevant red, green, and blue information for that pixel. The alpha channel data is not required in this example, so you can safely ignore it:
var r = pixelData.data[i]; // red channel var g = pixelData.data[i + 1]; // green channel var b = pixelData.data[i + 2]; // blue channel
Now that you have this information, you need to convert it to grey. A number of methods are available to do this, the simplest of which is to calculate the average colour across all three values:
var averageColour = (r + g + b) / 3;
You then set each red, green, and blue value for each of the pixels to be this new average colour:
pixelData.data[i] = averageColour; pixelData.data[i + 1] = averageColour; pixelData.data[i + 2] = averageColour;
Finally, you need to write the newly manipulated image and its data to the actual display canvas context:
context.putImageData(pixelData, 0, 0);
Of course, right now this code doesn’t do anything: You need to ensure that the makeItGrey() function actually gets called.
Mimicking what you did in the second example, the function can simply be set up to be called at 33-millisecond intervals using the setInterval() function when the video is set to play:
video.addEventListener(‘play’, function() { setInterval(“makeItGrey()”, 33); }, false);
And presto, the video copy is displayed on the canvas but this time in greyscale (Figure 9.5).
Figure 9.5 A greyscale copy of the original video is displayed in the canvas element on the right.
There’s a lot more that you can do with canvas and with pixel manipulation. This chapter provided just a small insight into what you can achieve. If what you’ve read has piqued your interest, plenty of good online tutorials (e.g., http://www.html5rocks.com/en/tutorials/#canvas) and books are available that you can follow up with, and I encourage you to do so. Go and play!