I'm going to show you now how to do the same thing in Javascript, inside a browser.
(If the explanation is too long, you can scroll down directly to the demo, at the end of this post).
To sum it up :
1) grayscale an image in a <canvas>
2) load a colormap in another <canvas>
3) apply recolor algorithm (lookup from the grayscaled image in the colormap)
Javascript & the <canvas> object
In "HTML5" (whatever the name really implies), with a recent browser (Opera, Chrome, Firefox, Safari...), you can work with a special element : the <canvas>.A 2D canvas hold all the pixels of a raster image, as an element inside a webpage. The benefits of using a <canvas> object instead an <img> is the ability to access all the pixels of the image stored inside (the <img> is strictly passive, it can just display an image).
At the beginning, Javascript was slow. But now, you the common browsers are using some optimized JIT Javascript VM, allowing you to do some tasks previously only doable on desktop.
Image processing tasks are now possible on a (somewhat) recent computer, inside a web app.
The javascript code shwon here is going to implement the OpenCV algorithm in the previous post.
Representing color images in memory
First, you retrieve an array of all the pixels of the image, you then get a second array, the array of the pixels from the colormap (a pixel wide column of 256 pixels).
After that, it's simply a matter of iterating over all the pixels of the image, getting the luminosity level, seeking in the colormap the R,G,B triplet associated to that value, and writing it back in the array (you can find a descriptive image in the previous post : http://podeplace.blogspot.fr/2012/11/opencv-pseudocolors.html)
Color Image as seen by OpenCV : 3 colorplanes (Red, Green, Blue) |
Grayscale Image : OpenCV is using only one colorplane in memory |
The pixel array of the
Image en mémoire pour un <canvas> : 4 pixels de couleurs consécutifs (Rouge, Vert, Bleu, Alpha) pour chaque pixel coloré |
Convert a color canvas to grayscale
RGB to Gray Mix, via averaging |
You can find online several values to define the blending of the three values. For example, in the PAL/NTSC format(used by the analog TV), the luminance channel is defined as is :
RGB to Gray mix (closer to human perception) |
lum = 0.299 * red + 0.587 * green + 0.114 * blue
The CIE 1931 luminance is defined as :
Y = 0.2126 * red + 0.7152 * green + 0.0722 * blue
(The values used by OpenCV are Red : 0.212671, Green : 0.715160, and Blue : 0.072169).
As some other Javascript example over the web, I choose the following values :
brightness = 0.34 * red + 0.5 * green + 0.16 * blue
All those models take into account the human sensitivity for green.
Difference between the gray from averaging and gray from the previous physiological model. |
Algorithm in javascript
In our example, we can now grayscale the image at startup, and then work with that image to apply the colormap.When working with a canvas cvs and its associated context ctx, you can retrieve the array of specific image data via
var myImageData = ctx.getImageData(0, 0, cvs.width, cvs.height);
Inside that variable, there's a data array, storing the colored pixels in the following order [red, green, blue, alpha, red, green, blue, alpha,...].
In javascript, to avoid a slow DOM traversal when accessing the myImageData.data, you can detach that pixel array as a new var
var dataSrc = myImageData.data;
and access what's inside that dataSrc, for a quicker access.
Since that array is a flat 1-dimensional one, you can access the various pixel with a regular loop like that :
for(var y = 0; y < height; y++){
for(var x = 0; x < width; x++){
index = (x + y * width) * 4;
dataSrc[index+0] = ROUGE;
dataSrc[index+1] = VERT;
dataSrc[index+2] = BLEU;
//dataSrc[index+3] = ALPHA; //no modifications
}}
Don't change the alpha value here. The *4 is there because to get to the next pixel in memory, you need to jump over 4 values (RGBA).
If you want to apply a grayscale conversion on that image, you can write back the new luminosity value in all the red, green and blue channel, for display purpose (however, the image is going to be three time more luminous than the true RGB->Grayscale conversion, although only on a computer display).
Don't worry, when you work with that pixel array on the next stage, to apply the colormap, you are going to use the luminosity value of only one channel, since it's the same value on all three (and that's why, although the displayed image is not the "true" grayscaled one, you are going to have the true colored image in the end).
If we choose to only store the grayscale value in one channel, the red one for example, the displayed image would be only in tint of red (and it feels a bit strange to consider a red-only, or blue-only or green-only image as "grayscale").
That kind of pixel access is the basis to all image operations and filters in Javascript (and more generally in all general purpose language when dealing with images).
After all the modifications are done, don't forget to write back those pixel in the image !
myImageData.data = dataRes; //reattache the variable
ctx.putImageData(myImageData,0,0); //write it back in the canvas
In the provided example, we load a regular JPEG picture, grayscale it on the click of a button, and then recolor it when the user click on one colormap. You need to reload the page if you want to use another colormap, to trigger the proper color -> grayscale conversion. If not, you are going to apply various colormap on already modified pictures, leading to some unpleasant effects in the end.
Demonstration in javascript :
Link of the project on GitHub : https://github.com/Pseudopode/javascript_pseudocolors and the .zip archive of the project.
Aucun commentaire:
Enregistrer un commentaire