[SPIP 1.9 et GD2] Nous allons ici exploiter deux techniques présentées récemment : l’analyse de l’histogramme et la modification de la courbe d’une image. Les filtres présentés ici vont réaliser l’automatisme suivant :
— dans un premier temps, analyser l’histogramme d’une image,
— en fonction de ces résultats, corriger la courbe, de façon à « rééquilibrer » les couleurs de l’image.
Dans Photoshop, cette fonctionnalité se nomme « Niveaux automatiques » (Auto Levels). Lorsque l’on retouche des photographies numériques, cette opération est incroyablement pratique : elle rééquilibre les images avec efficacité, et pour l’utilisateur moyen, il n’est souvent pas besoin d’aller plus loin pour améliorer la qualité de l’image.
Pour un rappel sur la correction des niveaux, vous pouvez lire ce tutorial en ligne.
Rééquilibrer l’intensité lumineuse
Commençons par analyser notre image d’origine :

- Une photo de cymagenpdx sur Flickr
Nous pouvons afficher avec SPIP son histogramme :

Le code pour afficher l’image et son histogramme est, rappelons-le :
[(#FICHIER|image_reduire{400})]
[(#FICHIER|image_reduire{400}|image_histo)]
L’information qui nous intéresse ici est la courbe grise, qui représente la répartition des niveaux d’intensité lumineuse. Remarquons en particulier que, sur le côté droit de la courbe, une large plage ne comporte aucun pixel. Entre les niveaux d’intensité 210 et 255 (proches du blanc, donc), il n’y a aucun pixel de l’image contenant ces valeurs. Dit autrement : l’image ne comporte pas de zones « très » claires.
De manière moins importante, on peut considérer que l’image comporte très peu de zones « très » sombres.
Le but du filtre va consister à corriger cela, en « décalant » l’intensité des pixels, de façon à ce que les zones les plus claires de l’image deviennent quasiment « blanches », et les zones les plus sombres deviennent quasiment « noires ».
Cette transformation se réalise très simplement selon le principe de notre filtre de modification de la courbe de l’image.
Grosso modo, notre nouveau filtre va simplement combiner les méthodes de deux filtres déjà présentés dans ces pages : d’abord extraire les informations de l’histogramme, puis modifier la courbe de l’image en fonction de ces informations.
Voici notre nouveau filtre :
function image_niveaux_gris_auto($im, $limite=1000) {
// $limite=1000: les nuances min et max representent 0,1% du total
$image = valeurs_image_trans($im, "niveaux_gris_auto-$limite");
if (!$image) return("");
$x_i = $image["largeur"];
$y_i = $image["hauteur"];
$im = $image["fichier"];
$dest = $image["fichier_dest"];
$creer = $image["creer"];
if ($creer) {
$im = $image["fonction_imagecreatefrom"]($im);
// Calculer les poids des differentes nuances
for ($x = 0; $x < $x_i; $x++) {
for ($y=0; $y < $y_i; $y++) {
$rgb = ImageColorAt($im, $x, $y);
$a = ($rgb >> 24) & 0xFF;
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$a = (127-$a) / 127;
$a=1;
$gris =
round($a*
($r+
$g+
$b) /
3);
$val_gris[$gris] ++;
}
}
$total = $x_i * $y_i;
for ($bas = 0; $somme_bas < $total/$limite; $bas++) {
$somme_bas += $val_gris[$bas];
}
for ($haut = 255; $somme_haut < $total/$limite ; $haut--) {
$somme_haut += $val_gris[$haut];
}
$courbe[0] = 0;
$courbe[255] = 255;
$courbe[$bas] = 0;
$courbe[$haut] = 255;
// Calculer le tableau des correspondances
while (list($key,
$val) =
each($courbe)) {
if ($key > 0) {
$key1 = $key_old;
$val1 = $val_old;
$prop = ($val - $val1) / ($key-$key1);
for ($i = $key1; $i < $key; $i++) {
$valeur =
round($prop *
($i -
$key1) +
$val1);
$courbe[$i] = $valeur;
}
$key_old = $key;
$val_old = $val;
} else {
$key_old = $key;
$val_old = $val;
}
}
// Appliquer les correspondances
$im2 = imagecreatetruecolor($x_i, $y_i);
@imagealphablending($im2, false);
@imagesavealpha($im2,true);
$color_t = ImageColorAllocateAlpha( $im2, 255, 255, 255 , 0 );
imagefill ($im2, 0, 0, $color_t);
for ($x = 0; $x < $x_i; $x++) {
for ($y=0; $y < $y_i; $y++) {
$rgb = ImageColorAt($im, $x, $y);
$a = ($rgb >> 24) & 0xFF;
$r = ($rgb >> 16) & 0xFF;
$v = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$r = $courbe[$r];
$v = $courbe[$v];
$b = $courbe[$b];
$color = ImageColorAllocateAlpha( $im2, $r, $v, $b , $a );
imagesetpixel ($im2, $x, $y, $color);
}
}
$image["fonction_image"]($im2, "$dest");
imagedestroy($im2);
imagedestroy($im);
}
$class = $image["class"];
if (strlen($class) >
1) $tags=
" class='$class'";
$tags = "$tags alt='".$image["alt"]."'";
$style = $image["style"];
if (strlen($style) >
1) $tags=
"$tags style='$style'";
return "<img src='$dest'$tags />";
}
On applique ce filtre ainsi :
[(#FICHIER|image_niveaux_gris_auto|image_reduire{400})]
[(#FICHIER|image_niveaux_gris_auto|image_reduire{400}|image_histo)]
On peut indiquer une valeur de « limite » (par défaut, 1000). Cela signifie que l’on corrige l’image en « brûlant » les niveaux dont le nombre de pixel est inférieur à 1/1000 du nombre total de pixels (c’est le réglage standard, d’ailleurs, de Photoshop).
Voici l’image obtenue :

et son histogramme :

On constate que la courbe grise » a été décalée vers la droite. On a renforcé, ici, le contraste de l’image, sans modifier les dominantes de couleur (l’image a toujours une forte dominante rouge).
N.B. L’intérêt d’un tel filtre par rapport à un simple renforcement du contraste lumineux, c’est que si l’image d’origine est déjà correctement équilibrée, alors elle n’est pas modifiée. La correction de la luminosité se fait en fonction de l’image elle-même, et non de valeurs passées a priori par le webmestre.
Corriger les niveaux des couleurs
Le filtre précédent me semble le moins dangereux à utiliser en tant qu’automatisme sur un site Web : les dominantes de couleurs ne sont pas modifiées, on peut penser que le résultat sera une amélioration systématique des images, sans trop de risques de modifier sauvagement une « belle » image installée par un rédacteur doué en photographie...
Cependant, allons plus loin. Dans Photoshop, la fonction de niveau automatique ne concerne pas seulement le rééquilibrage de la luminosité, mais corrige en réalité indépendamment le niveau de chaque couche de couleur (rouge, vert, bleu).
C’est ce que réalise le filtre ci-dessous :
function image_niveaux_auto($im, $limite=1000) {
// $limite=1000: les nuances min et max representent 0,1% du total
$image = valeurs_image_trans($im, "niveaux_auto-$limite");
if (!$image) return("");
$x_i = $image["largeur"];
$y_i = $image["hauteur"];
$im = $image["fichier"];
$dest = $image["fichier_dest"];
$creer = $image["creer"];
if ($creer) {
$im = $image["fonction_imagecreatefrom"]($im);
// Calculer les poids des differentes nuances
for ($x = 0; $x < $x_i; $x++) {
for ($y=0; $y < $y_i; $y++) {
$rgb = ImageColorAt($im, $x, $y);
$a = ($rgb >> 24) & 0xFF;
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$a = (127-$a) / 127;
$a=1;
$gris =
round($a*
($r+
$g+
$b) /
3);
$val_r[$r] ++;
$val_v[$g] ++;
$val_b[$b] ++;
}
}
$total = $x_i * $y_i;
// Calculer le tableau des correspondances
// Rouge
for ($bas_r = 0; $somme_bas_r < $total/$limite; $bas_r++) {
$somme_bas_r += $val_r[$bas_r];
}
for ($haut_r = 255; $somme_haut_r < $total/$limite ; $haut_r--) {
$somme_haut_r += $val_r[$haut_r];
}
$courbe_r[0] = 0;
$courbe_r[255] = 255;
$courbe_r[$bas_r] = 0;
$courbe_r[$haut_r] = 255;
while (list($key,
$val) =
each($courbe_r)) {
if ($key > 0) {
$key1 = $key_old;
$val1 = $val_old;
$prop = ($val - $val1) / ($key-$key1);
for ($i = $key1; $i < $key; $i++) {
$valeur =
round($prop *
($i -
$key1) +
$val1);
$courbe_r[$i] = $valeur;
}
$key_old = $key;
$val_old = $val;
} else {
$key_old = $key;
$val_old = $val;
}
}
// Bleu
for ($bas_b = 0; $somme_bas_b < $total/$limite; $bas_b++) {
$somme_bas_b += $val_b[$bas_b];
}
for ($haut_b = 255; $somme_haut_b < $total/$limite ; $haut_b--) {
$somme_haut_b += $val_b[$haut_b];
}
$courbe_b[0] = 0;
$courbe_b[255] = 255;
$courbe_b[$bas_b] = 0;
$courbe_b[$haut_b] = 255;
while (list($key,
$val) =
each($courbe_b)) {
if ($key > 0) {
$key1 = $key_old;
$val1 = $val_old;
$prop = ($val - $val1) / ($key-$key1);
for ($i = $key1; $i < $key; $i++) {
$valeur =
round($prop *
($i -
$key1) +
$val1);
$courbe_b[$i] = $valeur;
}
$key_old = $key;
$val_old = $val;
} else {
$key_old = $key;
$val_old = $val;
}