Convertir une image couleur à grayscale avec le canvas HTML5
Comme la plupart des gens, vous avez certainement vu le film Les aventures de Tintin en 3D qui raconte l'histoire d'un jeune reporter roux d'à peu près 17 ans qui sait piloter une moto comme un champion et un avion en situation catastrophique. Moi, je ne l'ai vu que récemment avant qu'il disparaisse des salles de cinéma (avec les lunettes BCBG). Ce qui m'a le plus impressionné, c'est l'hallucinante qualité de l'image 3D et les détails plus vrais que nature reproduits par ordinateur. Bientôt, l'ère des vrais acteurs payés à coup de millions sera révolue et constituera un genre à part dans l'industrie. Il n'y a qu'à imaginer quel cachet aurait demandé Pauline Marois pour jouer le rôle secondaire de la Castafiore pour comprendre l'étendue du génie économique de Spielberg et Jackson.
Tout ça pour dire que le film avait un facteur wow gigantesque, du jamais vu. Comme programmeur, nul besoin de dire que je suis incapable de produire une affiche avec une palette de couleurs si éblouissante. Par contre, mes démarches d'autoformation avec le canvas HTML5 et la manipulation d'images m'ont intéressé à voir comment on pouvait convertir une image couleur en niveaux de gris (grayscale). C'est moins impressionnant mais l'effet sobre fait ressortir les jeux de lumière, comme dans une photo en noir et blanc.
Voici le code final, je vous explique les nouvelles notions ensuite.
var canvas = $('#myCanvas');
var context = canvas.get(0).getContext('2d');
var img = new Image();
img.src = 'images/tintin-3d.jpg';
$(img).load(
function(){
context.drawImage(img, 0, 0);
var imageData = context.getImageData(0, 0, canvas.width(), canvas.height())
var pixels = imageData.data;
var nbPixels = pixels.length;
for(var i=0 ; i<nbPixels ; i++){
// moyenne RGB pour obtenir le ton de gris
var average = (pixels[i*4] + pixels[i*4+1] + pixels[i*4+2]) / 3;
pixels[i*4] = average;
pixels[i*4+1] = average;
pixels[i*4+2] = average;
// On n'y touche pas car on ne veut pas appliquer de transparence
// pixels[i*4+3] = pour l'alpha
}
context.putImageData(imageData, 0, 0);
}
);
Ici, j'ai fait exprès pour que les dimensions du canvas soient exactement de la même grandeur que l'image à manipuler. Lorsqu'on récupère les informations de l'image, on se trouve à récupérer un long tableau qui décrit chaque pixels un à un. Chacun comprend quatre propriétés soient le rouge, vert, bleu et l'alpha (transparence) qui sont stockés de façon linéaire dans le tableau. Donc le premier pixel correspond aux valeurs des indices 0 à 3 où les trois premiers sont pour le RGB et le quatrième pour l'alpha. Pour le pixel suivant, on se déplace 4 indices plus loin (offset de 4) pour travailler sur les indices 4 à 7. On finit par faire une itération sur l'ensemble du tableau pour remplacer chaque pixel par un ton de gris obtenu en calculant la moyenne de chaque couleur RGB. C'est aussi simple que ça.
A noter que la moyenne arithmétique n'est pas une des meilleures techniques pour la conversion d'image couleur en image noir et blanc ;)
Quelques informations (et formules de conversion) ici :
http://fr.wikipedia.org/wiki/Niveau_de_gris
Je pense en toute honnêteté que ce code est plus simple :
http://dl.dropbox.com/u/11137205/grayscale.html
(ça n'empêche pas d'utiliser un pour récupérer l'image converti)
De plus, c'est beaucoup moins gourmand!
Oui si on veut le faire par CSS mais le but du billet était de faire la démonstration à l'aide d'un canvas HTML5.
C'est quand même super de pouvoir comparer différentes techniques :-)