1

Modifier les courbes d’une image

24 juin 2006
par ARNO*

[SPIP 1.9 et GD2] Nous allons ici réaliser un filtre qui reproduit une des plus utiles fonctions de Photoshop : la modification des courbes.

Si vous ne connaissez pas cette commande, je vous invite à lire cette présentation qui me semble très claire.

Le principe général consiste à prendre les valeurs de couleur (ou de niveau de gris), de 0 à 255 (en codage RVB), et à faire correspondre d’autres valeurs.

Dans la transformation ci-dessus, la courbe permet de décrire visuellement le passage des valeurs d’origine aux valeurs finales :
— le noir (0) reste noir (0) ;
— le blanc (255) reste blanc (255) ;
— les valeurs intermédiaires sont modifiées : par exemple, un gris de valeur 111 devient un gris de valeur 190 (beaucoup plus clair).

Ce qui donne, ici, une image donc les noirs et les blancs sont conservés, mais dont toutes les valeurs intermédiaires sont nettement éclaircies.

Cela n’est qu’un exemple parmi une infinité de possibilités : la courbe peut-être plus ou moins complexe, et même le noir et le blanc peuvent être transformés. Par ailleurs, il est important de noter qu’on peut agir sur les canaux rouge, vert et bleu simultanément, ou chacun indépendamment.

Les possibilités de modification en jouant avec ces courbes vont des plus discrètes (renforcer le constraste, légèrement teinter une image...) aux plus farfelues (inversion de couleurs, effets psychadéliques...).

Voici notre filtre :

  1. function image_courbe($im, $couche, $courb="") {
  2.  
  3.         $numargs = func_num_args();
  4.         $arg_list = func_get_args();
  5.         $texte = $arg_list[0];
  6.         for ($i = 1; $i < $numargs; $i++) {
  7.                 if (ereg("\=", $arg_list[$i])) {
  8.                         $nom_variable = substr($arg_list[$i], 0, strpos($arg_list[$i], "="));
  9.                         $val_variable = substr($arg_list[$i], strpos($arg_list[$i], "=")+1, strlen($arg_list[$i]));
  10.                         $courbe[$nom_variable] = $val_variable;
  11.                 }
  12.         }
  13.  
  14.         $image = valeurs_image_trans($im, "courbe-$couche-".serialize($courbe));
  15.         if (!$image) return("");
  16.  
  17.         $x_i = $image["largeur"];
  18.         $y_i = $image["hauteur"];
  19.        
  20.         $im = $image["fichier"];
  21.         $dest = $image["fichier_dest"];
  22.         $creer = $image["creer"];
  23.  
  24.         if ($creer) {
  25.                 $courbe[0] = 0;
  26.                 $courbe[255] = 255;
  27.        
  28.                 ksort($courbe);
  29.                 while (list($key, $val) = each($courbe)) {
  30.                         if ($key > 0) {
  31.                                 $key1 = $key_old;
  32.                                 $val1 = $val_old;
  33.                                 $prop = ($val - $val1) / ($key-$key1);
  34.                                 for ($i = $key1; $i < $key; $i++) {
  35.                                         $valeur = round($prop * ($i - $key1) + $val1);
  36.                                         $courbe[$i] = $valeur;
  37.                                 }
  38.                                 $key_old = $key;
  39.                                 $val_old = $val;
  40.                         } else {
  41.                                 $key_old = $key;
  42.                                 $val_old = $val;
  43.                         }
  44.                 }
  45.  
  46.                 $im = $image["fonction_imagecreatefrom"]($im);
  47.                 $im_ = imagecreatetruecolor($x_i, $y_i);
  48.                 @imagealphablending($im_, false);
  49.                 @imagesavealpha($im_,true);
  50.                 $color_t = ImageColorAllocateAlpha( $im_, 255, 255, 255 , 0 );
  51.                 imagefill ($im_, 0, 0, $color_t);
  52.  
  53.                 for ($x = 0; $x < $x_i; $x++) {
  54.                         for ($y=0; $y < $y_i; $y++) {
  55.                                 $rgb = ImageColorAt($im, $x, $y);
  56.                                 $a = ($rgb >> 24) & 0xFF;
  57.                                 $r = ($rgb >> 16) & 0xFF;
  58.                                 $v = ($rgb >> 8) & 0xFF;
  59.                                 $b = $rgb & 0xFF;
  60.                                
  61.                                 if ($couche == "rvb" OR $couche == "r") $r = $courbe[$r];
  62.                                 if ($couche == "rvb" OR $couche == "v") $v = $courbe[$v];
  63.                                 if ($couche == "rvb" OR $couche == "b") $b = $courbe[$b];
  64.                                
  65.                                 $color = ImageColorAllocateAlpha( $im_, $r, $v, $b , $a );
  66.                                 imagesetpixel ($im_, $x, $y, $color);                  
  67.                         }
  68.                 }
  69.  
  70.                 $image["fonction_image"]($im_, "$dest");
  71.                 imagedestroy($im_);
  72.                 imagedestroy($im);
  73.         }
  74.  
  75.         $class = $image["class"];
  76.         if (strlen($class) > 1) $tags=" class='$class'";
  77.         $tags = "$tags alt='".$image["alt"]."'";
  78.         $style = $image["style"];
  79.         if (strlen($style) > 1) $tags="$tags style='$style'";
  80.         return "<img src='$dest'$tags />";
  81. }

Télécharger

C’est l’un des filtres les plus simples que nous ayons créés jusqu’ici, car il ne nécessite aucun calcul complexe :
— la première partie du filtre analyse les valeurs décrivant la courbe, et calcule toutes les transformations de couleurs pour toutes les valeurs entre 0 et 255 ;
— la seconde partie, la double boucle habituelle, parcourt chaque pixel pour modifier la couleur en fonction de ces 255 valeurs. Pas de calcul : une simple correspondance dans un tableau.

Le filtre d’utilise ainsi :

  1. [(#FICHIER
  2.         |reduire_image{400}
  3.         |image_courbe{rvb, 62=30, 193=220})]

Télécharger

La première valeur passée indique si la transformation doit d’appliquer à toutes les couches à la fois (rvb) ou seulement à une des couches (r, v ou b).

Ensuite, on passe une série de valeurs (autant que l’on veut). Par exemple, ici, on indique que 62 devient 30, 193 devient 220. Le filtre calculera automatiquement toutes les valeurs intermédiaires.

Notez bien qu’il n’est pas nécessaire de préciser 0=0 et 255=255 pour décrire les extrêmités de la courbe : si vous ne les précisez pas, le filtre les ajoutera tout seul.

Enfin, contrairement à Photoshop, le filtre n’essaie pas de produire de belles courbes lissées : il créée tout bêtement des droites entres les points de la « courbe » ainsi décrite. Mais ça fonctionne tout de même très bien !

Voici une image pour tester notre filtre :

Les valeurs de l’exemple précédent donnent le résultat suivant :

On voit qu’ainsi, les contrastes sont renforcés (trop, il faudrait certainement utiliser des changements plus subtiles) sans que les blancs et les noirs soient « brûlés » (il y a encore des nuances dans les zones très sombres et très claires). Avec des valeurs moins violentes, ce type de courbe donne l’impression d’enlever une sorte de « voile blanc » de l’image.

Il est très facile, avec ce filtre, d’inverser les couleurs de l’image :

  1. [(#FICHIER
  2.         |reduire_image{400}
  3.         |image_courbe{rvb, 0=255, 255=0})]

Télécharger

Le blanc (255) devient noir (0) et le noir devient blanc.

On peut aussi chercher des traitements « typant » un peu plus les images :

  1. [(#FICHIER
  2.         |reduire_image{400}
  3.         |image_aplatir{png}
  4.         |image_courbe{v, 164=255}
  5.         |image_courbe{b, 84=184, 193=255}
  6.         |image_courbe{r, 63=141, 156=86})]

Télécharger

Comme on applique toute une série de traitements à une image JPEG, on force son passage en PNG avec image_aplatir, de façon à ne pas ajouter des artefacts de compression à chaque traitement.

Ensuite, on applique trois fois notre filtre, appliqué successivement à la couche verte, bleue puis rouge.

On obtient :

Évidemment, il ne faut pas considérer ce filtre dans le cadre d’une utilisation image par image : il s’agit ici de l’appliquer systématiquement à des images indéterminées (installées par les rédacteurs), grâce aux squelettes de SPIP. Il faut donc chercher et expérimenter, dans le but non pas d’« améliorer » des images (ce qui est l’utilisation typique de ce filtre dans Photoshop), mais d’appliquer un traitement graphique permettant de « typer » une interface graphique globale.

Qui êtes-vous ?
Votre message

Ce formulaire accepte les raccourcis SPIP [->url] {{gras}} {italique} <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.