1

Welcome to Spip-City

31 mars 2006
par ARNO*

[SPIP 1.9 et GD2] Adapté de la bande dessinée de Frank Miller, le film Sin City présente des images lourdement retravaillées en post-production : l’image est entièrement passée dans un noir et blanc très contrasté, à l’exception des zones rouges apparaissent comme des taches colorées au milieu d’une image totalement désaturée.

Voici comment réaliser un effet similaire, automatiquement, avec un filtre de SPIP.

Notre filtre se nomme image_sincity, et voici son code :

  1. function image_sincity($im)
  2. {
  3.         $image = valeurs_image_trans($im, "sincity");
  4.         if (!$image) return("");
  5.        
  6.         $x_i = $image["largeur"];
  7.         $y_i = $image["hauteur"];
  8.        
  9.         $im = $image["fichier"];
  10.         $dest = $image["fichier_dest"];
  11.         $creer = $image["creer"];
  12.        
  13.         if ($creer) {
  14.                 $im = $image["fonction_imagecreatefrom"]($im);
  15.  
  16.                 $im_ = imagecreatetruecolor($x_i, $y_i);
  17.                 @imagealphablending($im_, false);
  18.                 @imagesavealpha($im_,true);
  19.                 $color_t = ImageColorAllocateAlpha( $im_, 255, 255, 255 , 127 );
  20.                 imagefill ($im_, 0, 0, $color_t);
  21.  
  22.                 $tol = 0.05 ;
  23.                 for ($x = 0; $x < $x_i; $x++) {
  24.                         for ($y=0; $y < $y_i; $y++) {
  25.                        
  26.                                 $rgb = ImageColorAt($im, $x, $y);
  27.                                 $a = ($rgb >> 24) & 0xFF;
  28.                                 $r = ($rgb >> 16) & 0xFF;
  29.                                 $g = ($rgb >> 8) & 0xFF;
  30.                                 $b = $rgb & 0xFF;
  31.                                
  32.                                 if ($a < 127) {
  33.                                         $hsv = image_rgb2hsv($r,$g,$b);
  34.                                         $h = $hsv["h"];
  35.                                         $s = $hsv["s"];
  36.                                         $v = $hsv["v"];
  37.                                        
  38.                                         if ($h < $tol OR $h > 1-$tol) {
  39.                                                 if ($h < $tol) {
  40.                                                         $dist = ($tol-$h);
  41.                                                 }
  42.                                                 else if ($h > 1-$tol) {
  43.                                                         $dist = ($h - (1-$tol));
  44.                                                 }
  45.                                                 $s = $s * ($dist/$tol);
  46.                                                 if ($s > 1) $s = 1;
  47.                                                 $h = 0;
  48.                                         } else {
  49.                                                 $s = 0;
  50.                                         }
  51.                                        
  52.                                         $v = 2*($v - 0.6) + 0.6;
  53.                                        
  54.                                         if ($v > 1) $v=1;
  55.                                         if ($v < 0) $v =0;
  56.                                        
  57.                                         $rgb = image_hsv2rgb($h,$s,$v);
  58.                                         $r = $rgb["r"];
  59.                                         $g = $rgb["g"];
  60.                                         $b = $rgb["b"];
  61.                                 }
  62.                                 $color = ImageColorAllocateAlpha( $im_, $r, $g, $b , $a );
  63.                                 imagesetpixel ($im_, $x, $y, $color);                           }
  64.                 }              
  65.                 $image["fonction_image"]($im_, "$dest");                }
  66.  
  67.         $class = $image["class"];
  68.         if (strlen($class) > 1) $tags=" class='$class'";
  69.         $tags = "$tags alt='".$image["alt"]."'";
  70.         $style = $image["style"];
  71.        
  72.         return "<img src='$dest'$tags />";
  73. }

Télécharger

Il s’utilise, comme tout filtre d’image de SPIP, sur une image (logo ou document joint). Par exemple :

Ce filtre étant relativement lourd en calculs, on pense à réduire les dimensions de l’image avant le traitement.

Le principe est similaire à notre filtre de (dé)saturation : on va passer les valeurs RVB de chaque pixel en HSV (hue, saturation, value) de façon à accéder immédiatement aux informations qui nous intéressent :
— si la teinte de l’image est rouge (hue=0), alors on sature l’image ;
— sinon on désature complètement l’image (s=0), c’est-à-dire qu’on supprime l’information de couleur ;
— dans tous les cas, on renforce fortement le contraste entre les zones claires et les zones foncées (on se rapproche de v=0 et de v=1).

Commençons par traiter les zones « rouges ». Il s’agit du test :

  1. if ($h < $tol OR $h > 1-$tol) {
  2.         if ($h < $tol) {
  3.                 $dist = ($tol-$h);
  4.         }
  5.         else if ($h > 1-$tol) {
  6.                 $dist = ($h - (1-$tol));
  7.         }
  8.         $s = $s * ($dist/$tol);
  9.         if ($s > 1) $s = 1;
  10.         $h = 0;
  11. }

Télécharger

sachant que nous avons réglé la tolérance $tol à 0.05 un peu plus tôt dans le code. Nous testons donc si la teinte ($h) est comprise entre 0 et 0.05 ou entre 0.95 et 1. En effet, même avec beaucoup de bonne volonté, les « rouges » de vos images ne seront certainement pas exactement dans la teinte « 0 » ; nous nous réservons donc une petite tolérance.

Ici, nous pourrions simplement nous contenter de rectifier la valeur de rouge (lorsqu’on est « rouge », on ne veut que notre teinte de rouge de référence), en forçant : $h=0;

Cela n’est pas suffisant, et provoque d’affreuses taches rouges, avec des bords très pixelisés (les artefacts JPEG renforçant encore l’aspect catastrophique), puisqu’on passe systématiquement de zones parfaitement rouges à d’autres parfaitement désaturées.

D’où l’introduction d’un petit calcul de « distance » entre la véritable valeur de la couleur et le rouge de référence, et l’application de cette distance à la saturation de la couleur :
— si la couleur est quasiment identique au rouge de référence, on conserve la saturation (on a donc le rouge de référence très apparent ;
— plus on s’éloigne de cette valeur de référence, moins le pixel est saturé, c’est-à-dire qu’on créé ainsi un lissage entre les zones rouges et les autres couleurs (qui seront totalement désaturées dans la ligne qui suit).

Dans le cas où l’on n’est pas dans une couleur proche de la valeur de référence, on se contente de totalement désaturer le pixel (aucune information de couleur) ; $s = 0;

L’essentiel de notre filtre est réalisé :
— les zones rouges conservent leur couleur, dont la teinte est forcée au rouge de référence ;
— les autres teintes sont passées entièrement en noir et blanc.

Pour s’approcher encore de l’esthétique de Sin City, il nous reste à renforcer le contraste, de manière très forte, quitte à « brûler » les noirs et les blancs :

  1. $v = 2*($v - 0.6) + 0.6;
  2.                                        
  3. if ($v > 1) $v=1;
  4. if ($v < 0) $v =0;

Télécharger

Ici, on multiplie le contraste par deux.

Notez qu’on a décalé les valeurs de contraste non pas par rapport au gris 50%, mais en décalant par rapport au gris 60%. De cette façon, dans le même calcul, on a également renforcé les zones sombres (Sin City privilégie les zones sombres.

Un essai sur une autre image :

Une photo de cymagenpdx sur Flickr.

donne ceci :

Pour conclure, signalons que ce filtre est d’une utilisation très délicate en pratique : la qualité des images et le passage par le format PNG influent énormément sur le résultat final. Parmi les difficultés, signalons :
— la mise en valeur des imperfections du format JPEG (les artefacts de compression deviennent extrêmement visibles sur certaines images) ;
— la mise en valeur, sur de nombreux visages, de zones rouges qui, du coup, deviennent très inélégantes (visage à l’aspect « rougeaud »).

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.