Contactez-nous

1

Afficher l’histogramme d’une image

24 juin 2006
par ARNO*

[SPIP 1.9 et GD2] Toujours à la recherche d’applications inédites des filtres de SPIP, je vous propose aujourd’hui de franchir une nouvelle étape : un filtre qui ne sert rigoureusement à rien !

À rien pour traiter une image, certes, mais dans une galerie de photos, ça peut produire un petit effet assez pro... Et il n’est pas impossible que, par la suite, nous trouvions un usage à la méthode développée ici1.

L’idée est d’extraire d’une photographie les informations de couleur de tous ses pixels et de les afficher dans un graphique synthétique, que l’on nomme habituellement un histogramme. Partons de l’image suivante :

elle nous donne l’histogramme suivant :

Le graphique se lit ainsi :
— en fond d’image, la courbe grise indique la répartition des niveaux de gris ;
— par dessus, trois courbes colorées indiquent les répartitions de rouge, vert et bleu ;
— pour chaque courbe, l’axe horizontal de gauche à droite parcourt les intensités de 0 à 255 ; pour le gris, par exemple, 0 est le noir, 255 est le blanc ; et verticalement, la « hauteur » de la courbe indique la densité de ce niveau de couleur, c’est-à-dire le nombre de pixels qui correspondent à ce niveau.

Par exemple, ici, pour le gris comme pour les trois composantes, on a des pics d’intensité situés aux alentours de l’intensité 200 ; ce qui signifie qu’un grand nombre de pixels ont une couleur proche du gris 78% (relativement clair, donc). On constate également qu’il n’y a quasiment pas de pixels avec des intensités supérieures à 220 ; ce qui signifie qu’il n’y a quasiment pas de pixels très clairs dans l’image (l’image n’a pas de zones très claires ou blanches).

Voici notre filtre image_histo :

  1. function image_histo($im) {
  2.  
  3. $image = valeurs_image_trans($im, "courbes", "gif");
  4. if (!$image) return("");
  5.  
  6. $x_i = $image["largeur"];
  7. $y_i = $image["hauteur"];
  8. $im = $image["fichier"];
  9. $dest = $image["fichier_dest"];
  10. $creer = $image["creer"];
  11.  
  12. if ($creer) {
  13. $im = $image["fonction_imagecreatefrom"]($im);
  14. $im_ = imagecreatetruecolor(258, 130);
  15. @imagealphablending($im_, false);
  16. @imagesavealpha($im_,true);
  17. $color_t = ImageColorAllocateAlpha( $im_, 255, 255, 255 , 0 );
  18. imagefill ($im_, 0, 0, $color_t);
  19. $col_poly = imagecolorallocate($im_,0,0,0);
  20. imagepolygon($im_, array ( 0, 0, 257, 0, 257, 129, 0,129 ), 4, $col_poly);
  21.  
  22. for ($x = 0; $x < $x_i; $x++) {
  23. for ($y=0; $y < $y_i; $y++) {
  24.  
  25. $rgb = ImageColorAt($im, $x, $y);
  26. $a = ($rgb >> 24) & 0xFF;
  27. $r = ($rgb >> 16) & 0xFF;
  28. $g = ($rgb >> 8) & 0xFF;
  29. $b = $rgb & 0xFF;
  30.  
  31. $a = (127-$a) / 127;
  32. $a=1;
  33.  
  34. $gris = round($a*($r+$g+$b) / 3);
  35. $r = round($a*$r);
  36. $g = round($a*$g);
  37. $b = round($a*$b);
  38.  
  39. $val_gris[$gris] ++;
  40. $val_r[$r] ++;
  41. $val_g[$g] ++;
  42. $val_b[$b] ++;
  43. }
  44. }
  45. $max = max( max($val_gris), max($val_r), max($val_g), max($val_b));
  46. $rapport = (127/$max);
  47.  
  48. $gris = imagecolorallocate($im_, 160, 160, 160);
  49. for ($i = 0; $i < 256; $i++) {
  50. $val = 127 - round(max(0,$val_gris[$i]) * $rapport); imageline ($im_, $i+1, 128, $i+1, $val+1, $gris);
  51. }
  52. $bleu = imagecolorallocate($im_, 0, 0, 255);
  53. for ($i = 0; $i < 256; $i++) {
  54. $val = 127 - round(max(0,$val_b[$i]) * $rapport);
  55. if ($i==0) imagesetpixel ($im_, $i+1, $val+1, $bleu);
  56. else imageline($im_, $i, $val_old+1, $i+1, $val+1, $bleu);
  57. $val_old = $val;
  58. }
  59. $green = imagecolorallocate($im_, 0, 255, 0);
  60. for ($i = 0; $i < 256; $i++) {
  61. $val = 127 - round(max(0,$val_g[$i]) * $rapport);
  62. if ($i==0) imagesetpixel ($im_, $i+1, $val+1, $green);
  63. else imageline($im_, $i, $val_old+1, $i+1, $val+1, $green);
  64. $val_old = $val;
  65. }
  66. $rouge = imagecolorallocate($im_, 255, 0, 0);
  67. for ($i = 0; $i < 256; $i++) {
  68. $val = 127 - round(max(0,$val_r[$i]) * $rapport);
  69. if ($i==0) imagesetpixel ($im_, $i+1, $val+1, $rouge);
  70. else imageline($im_, $i, $val_old+1, $i+1, $val+1, $rouge);
  71. $val_old = $val;
  72. }
  73.  
  74. $image["fonction_image"]($im_, "$dest");
  75. imagedestroy($im_);
  76. }
  77.  
  78. return "<img src='$dest' style='width: 258px; height: 130px;' />";
  79. }

Télécharger

La première partie détermine le nom et gère le système de cache pour l’image résultante. La seule particularité ici est que l’on travaille avec une image qui ne découle pas directement, pour ses dimensions, de l’image d’origine. C’est un graphique de taille fixe que l’on créée.

La double boucle, habituelle, parcourt l’image pixel par pixel :

  1. for ($x = 0; $x < $x_i; $x++) {
  2. for ($y=0; $y < $y_i; $y++) {
  3.  
  4. $rgb = ImageColorAt($im, $x, $y);
  5. $a = ($rgb >> 24) & 0xFF;
  6. $r = ($rgb >> 16) & 0xFF;
  7. $g = ($rgb >> 8) & 0xFF;
  8. $b = $rgb & 0xFF;
  9.  
  10. $a = (127-$a) / 127;
  11. $a=1;
  12.  
  13. $gris = round($a*($r+$g+$b) / 3);
  14. $r = round($a*$r);
  15. $g = round($a*$g);
  16. $b = round($a*$b);
  17.  
  18. $val_gris[$gris] ++;
  19. $val_r[$r] ++;
  20. $val_g[$g] ++;
  21. $val_b[$b] ++;
  22. }
  23. }
  24. $max = max( max($val_gris), max($val_r), max($val_g), max($val_b));
  25. $rapport = (127/$max);

Télécharger

On stocke les informations d’intensité dans des tableaux ($val_gris, $val_r...). À la fin de la double boucle, on détermine la valeur maximale de tous ces tableaux, de façon à pouvoir, ensuite, dessiner la valeur maximale à 127 pixels du bas de l’image.

C’est dans un deuxième temps, dans quatre boucles successives, de 0 à 255, que l’on parcourt les niveaux d’intensité pour dessiner nos quatre courbes.

Voici un autre exemple :

qui donne :

  • ced
    Septembre 2006

    Merci pour le boulot !

  • besoin d’aide
    Décembre 2006

    bonjour pour vous ; j’aimerai bien savoir un programme pour historamme d’image avec langage c merci

  • Elioscope
    Janvier 2007

    Voila une belle maniére de programmer des trucs inutiles

  • Stef
    Février 2007

    Une fonction qui me sera bien utile, ne serait-ce que pour évaluer des photos non retouchées, posées sur le site un peu trop rapidement. Grand Merci, je l’adopte !

  • Juillet 2007

    Je ne trouve pas comment modifier la couleur de fond de l’histogramme ? j’aimerai virer le blanc pour le remplacer par du transparent. C’est jouable ? Et si ce n’est pas jouable en changer la couleur ?

  • Gezus
    Avril 2008

    Salut et merci pour cette contrib bien pratique ! J’ai intégré l’histogramme au code de la galerie Walma, cela marche à merveille. Cependant j’aimerais savoir si quelqu’un à une idée de la lourdeur ou du temps d’exécution coté serveur pour le calcul de cet histogramme. Fait-il appel à des fonctions des librairies GD2 ou Imagick ? ou simplement en Javascript brut ?

    Pour voir l’intégration faite à Walma : http://gezus.homelinux.com/spip.php?page=walma&id_article=129

    D’avance merci

  • Gezus
    Avril 2008

    Salut, réponse à la question posée plus haut : Comment changer la couleur de fond de l’histogramme ?

    Reponse modifier cette ligne : (ligne 17)

    $color_t = ImageColorAllocateAlpha( $im_, 255, 255, 255 , 0 ) ;

    Si vous mettez 55 à la place de 255 partout cela donne un fond gris foncé à l’histo... après c’est à modifier à votre guise.

  • ARNO*
    Avril 2008

    @Gesuz

    C’est en GD2. La lourdeur du calcul est relativement importante ; elle dépend des dimensions de l’image, puisqu’on extrait la couleur de chaque point de l’image. Cependant, une fois que le calcul est réalisé pour une image, le résultat est sauvegardé dans un fichier qui n’est plus recalculé. Donc aucune lourdeur pour le serveur une fois que ça a été calculé une fois.

  • Gezus
    Avril 2008

    Royal Arno ! Merci pour tes précisions, très bonne idée de stocker l’histogramme pour ne pas le recalculer.
    - Puis-je te demander comment est stocké l’histogramme ? si c’est l’image qui est stockée, ou ? si ce sont les données brutes de l’histogramme, sont elles récupérables par une fonction portant sur le document ?

    - Aussi, si je comprend bien l’interêt de passer le filtre image_reduire400 avant le filtre image_histo permet de calculer l’histogramme sur une réduction de l’image à 400 pixels : Donc cela allège le travail de calcul de l’histogramme (mais celui-ci est un peu plus approximatif du coup).

    Merci pour ton aide.

    PS : je ne sais pas si c’est toi Arno qui est à l’origine ce bien joli site "Paris-Beyrouth", pour dire que c’est une réussite tant au plan graphique que de son contenu.

  • ARNO*
    Avril 2008

    @Gezus

    De mémoire et en relisant mon code ci-dessus, c’est le fichier GIF résultant qui est stocké. Pas les données brutes de l’histogramme, qui ne sont exportées nulle part.

    En revanche, il est possible de s’inspirer du principe exposé ici pour travailler automatiquement à partir des données de l’histogramme. C’est par exemple le principe exposé dans « Correction automatique ».

    Pour la réduction, oui c’est une bonne idée. Note que si ton image fait, au final, 400x300 pixels, ça te fait quand même un calcul sur 120 000 pixels. On réparti les informations de ces 120 000 pixels sur 256 échantillons. Donc oui on perd en précision, mais le résultat ne doit pas être non plus aberrant.

  • zeina ghannoum
    Mai 2008

    salut ! j’aimerai bien savoir un programme pour histogramme d’image en matlab

  • zelo
    Janvier 2009

    Intéressant, je viens de coder un truc du même genre mais moins sophistiqué (avec la librairie artichow).

    http://www.electronika.fr/blog/?p=301

    @+

  • denys
    Mars 2009

    Il me semble que le code d’affichage d’un histogramme n’est pas complet. Aurais-tu la possibilité de m’indiquer l’implémentation de cette fonction dans un script php.

    Merci d’avance

  • lili
    Mars 2009

    je voudré ce meme code avec matlab 6p5 si vous pouviez m’aidez ça se seré trop sympa de votr part chui vrément dans le pétrin !! merci

  • Tit’Annick
    Janvier 2010

    comment coder un calcul d’histogramme ?

  • rabiaa
    Février 2010

    bonjour, j’ai un probleme d’extraction des composante connexe et la segmentation d’un nom arabe (chaque lettre a part)d’une image en matlab

  • kamel
    Juin 2011

    je veux savoir comment on peut programmer un histogramme d’une image

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.