Code source du projet de traitement des images en JavaScript

Voir le résultat  Retour à la page du projet

Afficher/Masquer les commentaires

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8"/>
<title>Traitement d'images</title>
</head>
<body>

L'interface utilisateur est composée :

L'élément img contient l'image à traiter.

L'élément canvas est l'élément dans lequel vont se faire les traitements : on ne peut traiter des images que dans ce type d'élément, en particulier il est impossible de traiter directement une image dans un élément img.

<p>
<img id="monImage" src="image.jpeg">
&nbsp;
<canvas id="monCanvas"></canvas>
</p>
<p>

Boutons qui permettent d'appeler la fonction de traitement désirée.

<input id="btnGris" type="button" value="Niveaux de gris"/>&nbsp;
<input id="btnRetourne" type="button" value="Retournement"/>&nbsp;
<input id="btnFloute" type="button" value="Floutage"/>
</p>

Le code source du programme en JavaScript de traitement d'image début ici :

<script>

L'instruction document.getElementById permet d'obtenir des variables qui représentent les éléments de l'interface utilisateur et ainsi pouvoir les manipuler par programmation.

Avec ces lignes de programme, chaque bouton devient opérationnel pour réaliser le traitement en appelant la fonction correspondante.

	var btnGris=document.getElementById("btnGris");
	var btnRetourne=document.getElementById("btnRetourne");
	var btnFloute=document.getElementById("btnFloute");

	btnGris.onclick=gris;
	btnRetourne.onclick=retourne;
	btnFloute.onclick=floute;

Variables représentant l'élément img et l'élément canvas.

	var image = document.getElementById("monImage");
	var canvas = document.getElementById("monCanvas");  

Pour pouvoir agir sur un élément canvas il faut obtenir un contexte qui donne accès à un ensemble d'instructions. Ici nous utilisons un contexte "2d" qui permet de manipuler le canvas avec un système de repèrage des pixels en deux dimensions.

	var ctx = canvas.getContext("2d");

Avant de mettre en place les différents éléments pour traiter l'image, nous devons nous assurer qu'elle est entièrement chargée, en particulier pour être sûr que les propriétés width et height sont bien définies.

	image.onload=function(){

Nous mettons le canvas aux dimensions de l'image.

Ensuite nous "dessinons" l'image dans le canvas, ce qui revient à copier le contenu de l'élément img dans le canvas : à ce stade aucun traitement n'est réalisé.

		canvas.width=image.width;
		canvas.height=image.height;
		ctx.drawImage(image,0,0);

L'image affichée dans le canvas est constitué de pixels, chaque pixel est codé sur 4 octets :

L'instruction ctx.getImageData permet de récupérer un objet qui contient les informations d'image d'une zone du canvas. Il contient les dimensions de la zone et la propriété data qui est un tableau contenant les différents octets qui codent les pixels de l'image.

Ainsi imageDataSource.data contient l'ensemble des octets qui définissent les pixels du contenu du canvas qui sont en fait les pixels de l'image qu'on vient d'y recopier.

Les 4 premiers éléments de ce tableau sont les 4 premiers octets de l'information du pixel en haut à gauche, les 4 suivants sont pour le pixel à sa droite, et ainsi de suite jusqu'aux 4 derniers éléments qui donnent les informations pour le pixels en bas à droite.

Note : il n'y a pas de var devant imageDataSource, sinon la variable serait locale à la fonction et donc inacessible par la suite.

		imageDataSource = ctx.getImageData(0, 0,image.width,image.height);
	}

Passage en niveaux de gris

Nous utilisons la méthode de la moyenne arithmétique.

Nous commençons par créer un nouvel objet imageData nommé imageDataResult aux dimensions de l'image et qui sera utilisé pour construire le résultat du traitement.

Le tableau des informations des pixels sources est parcouru, à chaque tour de boucle, le compteur avance de 4 (chaque pixel est codé sur 4 octets).

Dans le tableau d'informations de pixels imageDataResult.data chacun des pixels est défini avec les trois composantes de couleur identiques : la moyenne des composantes rouge, vert et bleu du pixel d'origine.

	function gris(){
		var imageDataResult=ctx.createImageData(image.width,image.height);
		
		var gris;

		for(var i=0,l=imageDataSource.data.length;i<l;i=i+4){
			gris=(imageDataSource.data[i]+imageDataSource.data[i+1]+imageDataSource.data[i+2])/3;
			imageDataResult.data[i]=gris;
			imageDataResult.data[i+1]=gris;
			imageDataResult.data[i+2]=gris;
			imageDataResult.data[i+3]=imageDataSource.data[i+3]; //Transparence non modifiée
		}

Pour rendre le résultat visible à l'écran, il faut envoyer dans le canvas l'image dont les informations figurent dans imageDataResult qui vient d'être créé.

		ctx.putImageData(imageDataResult,0,0); 
	}

Position physique et position dans data

Habituellement lorsque l'on travaille avec des images les pixels se repèrent physiquement en plaçant l'origine en haut à gauche et du coup les ordonnées croissent vers le bas.

Par contre dans le tableau data les données des pixels sont rangées les unes à la suite des autres, ce qui rend toute manipulation qui fait intervenir les coordonnées impossible à gérer directement.

Pour contourner le problème voici la fonction conversion qui à partir des coordonnées physiques du pixel retourne la position dans data du premier octet qui défini ses caratéristiques.

	function conversion(x,y){
		return (y*image.width+x)*4;	
	}

Retournement

Le pixel en position (x,y) passe en position (x,hauteur-y).

L'utilisation de la fonction conversion est indispensable pour gérer facilement ce "jeu" sur les coordonnées.

	function retourne(){
		var imageDataResult=ctx.createImageData(image.width,image.height);
		var posSrc,posDest;

Les deux boucles imbriquées permettent de générer les coordonnées des pixels de l'image ligne par ligne.

		for(var y=0,h=image.height;y<h;y++){
			for(var x=0,w=image.width;x<w;x++){
				posSrc=conversion(x,y);
				posDest=conversion(x,h-y);
				//Recopie des 4 octets qui définissent le pixel.
				for(var i=0;i<4;i++)imageDataResult.data[posDest+i]=imageDataSource.data[posSrc+i];

			}
		}	

		ctx.putImageData(imageDataResult, 0, 0); 
	}

Floutage

Les deux boucles principales imbriquées permettent de parcourir les pixels de l'image avec le pas granularite. Ainsi (i,j) représente la position du pixel en haut et à gauche d'une carré de côté granularite.

	function floute(){
		var imageDataResult=ctx.createImageData(image.width,image.height);
		var granularite=7;
		var rouge,vert,bleu;
		var pos;

		for(var i=0;i<image.width-image.width%granularite;i=i+granularite){
			for(var j=0;j<image.height-image.height%granularite;j=j+granularite){
				rouge=0; vert=0; bleu=0;

Nous utilisons de nouveau deux boucles imbriquées pour générer les coordonnées des pixels du carré qui seront rendus ensuite dans une couleur uniforme.

Pour déterminer cette couleur, on cumule les valeurs des composantes rouge, vert et bleu de tous les pixels du carré.

				for(var x=i;x<i+granularite;x++){
					for(var y=j;y<j+granularite;y++){
						pos=conversion(x,y);
						rouge+=imageDataSource.data[pos];
						vert+=imageDataSource.data[pos+1];
						bleu+=imageDataSource.data[pos+2];
					}
				}

En divisant les cumuls de rouge, vert et bleu par granularite*granularite, rouge, vert et bleu contiennent maintenant les moyennes des composantes.

				rouge=rouge/(granularite*granularite);
				vert=vert/(granularite*granularite);
				bleu=bleu/(granularite*granularite);

Ces deux dernière boucles imbriquées permettent de définir tous les pixels de la zone carrée avec la même couleur "moyenne" calculée juste avant.

				for(var x=i;x<i+granularite;x++){
					for(var y=j;y<j+granularite;y++){
						pos=conversion(x,y);
						imageDataResult.data[pos]=rouge;
						imageDataResult.data[pos+1]=vert;
						imageDataResult.data[pos+2]=bleu;
						imageDataResult.data[pos+3]=imageDataSource.data[pos+3]; //Transparence identique
					}
				}
			}
		}
		ctx.putImageData(imageDataResult,0,0); 
	}
</script>
</body>
</html>