Jusqu’à maintenant j’utilisais Picasa pour trouver la couleur dominante des images. Le soft utilise le même algorithme que Google Image (logique) et donne de bons résultats à mon sens. Seulement j’avais besoin d’un truc standalone, soit un soft qui s’exécute en ligne de commande, soit directement une fonction (PHP dans mon cas).
Après avoir trouvé diverses fonctions, aucune ne me satisfaisant pleinement, j’ai faut une compilation de tout ça.
Le but est donc de calculer de quelle couleur de référence l’image s’approche le plus. Dans mon cas ma référence est (en RGB) :
$colors = array(
'red' => array(255,0,0),
'orange' => array(255,127,0),
'yellow' => array(255,255,0),
'green' => array(0,255,0),
'blue' => array(0,0,255),
'purple' => array(127,0,255),
'pink' => array(255,0,255),
);
…car ce sont les couleurs que donne Picasa, on peut bien sur ajouter n’importe quelle couleur.
Déjà je travaille dans l’espace HSV (Hue-Saturation-Value), ça donne de meilleurs résultats que dans l’espace RGB (Red-Green-Blue).
Ensuite le fonctionnement est somme toute assez simple : il y à un compteur par couleur de base, qu’on incrémente pour chaque pixel de l’inverse de la différence entre la couleur du pixel et la base, différence calculée en norme euclidienne.
Ainsi le compteur le plus haut correspond à la couleur dominante.
Voici donc ma fonction :
<?php
/**
* Copyright (C) 2011 Damien SOREL (Mistic)
* http://www.strangeplanet.fr
*
* This function is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*/
// conversion RGB -> HSV
function rgb2hsv ($RGB)
{
$var_R = $RGB[0]/255;
$var_G = $RGB[1]/255;
$var_B = $RGB[2]/255;
$var_Min = min($var_R, $var_G, $var_B);
$var_Max = max($var_R, $var_G, $var_B);
$del_Max = $var_Max - $var_Min;
$V = $var_Max;
if ($del_Max == 0)
{
$H = 0;
$S = 0;
}
else
{
$S = $del_Max / $var_Max;
$del_R = ((($var_Max - $var_R)/6) + ($del_Max/2)) / $del_Max;
$del_G = ((($var_Max - $var_G)/6) + ($del_Max/2)) / $del_Max;
$del_B = ((($var_Max - $var_B)/6) + ($del_Max/2)) / $del_Max;
if ($var_R == $var_Max) $H = $del_B - $del_G;
else if ($var_G == $var_Max) $H = (1/3) + $del_R - $del_B;
else if ($var_B == $var_Max) $H = (2/3) + $del_G - $del_R;
if ($H<0) $H++;
else if ($H>1) $H--;
}
return array($H*255, $S*255, $V*255);
}
// norme euclidienne
function distance($col1, $col2)
{
$distance = 0;
for($i=0 ; $i<3 ; $i++)
{
$diff[$i] = $col1[$i]/255-$col2[$i]/255;
$distance += $diff[$i]*$diff[$i];
}
if ($distance == 0) $distance = 0.01; // pour ne pas avoir l'inverse de 0
return sqrt($distance);
}
// fonctionne mère
function get_main_color($img, $points, $colors)
{
$width = imagesx($img);
$height = imagesy($img);
$scale = intval(max($width,$height)/$points);
// Pour chaque base on convertir en HSV et on initialise le compteur
foreach ($colors as $name => $base)
{
$results[$name] = 0;
$colors[$name] = rgb2hsv($base);
}
$colors_grey = 0; // Compteur pour les pixels gris (R=G=B)
$pixel_count = 0; // Compteur du nombre total de pixels analysés
for ($j=0; $j<$height; $j+=$scale)
{
for ($i=0; $i<$width; $i+=$scale)
{
// Couleur du pixel
$color = imagecolorat($img, $i, $j);
$r = ($color >> 16) &0xFF;
$g = ($color >> 8) &0xFF;
$b = $color &0xFF;
if ($r==$g and $g==$b)
{
$colors_grey++;
}
else
{
// On incrémente le compteur de chaque base
// de l'inverse de la distance à la couleur du pixel
foreach ($colors as $name => $base)
{
$color = rgb2hsv(array($r, $g, $b));
$results[$name] += 1 / distance($color, $base);
}
}
$pixel_count++;
}
}
if ($colors_grey == $pixel_count)
{
return 'grey';
}
else
{
asort($results);
$results = array_keys($results);
return $results[count($results)-1];
}
}
?>
Pour l’utiliser il suffit d’appeler la fonction get_main_color et lui fournissant une image, le nombre de points de contrôle (50 donne de bons résultats tout en ne prenant pas trop de temps, ça fait quand même 2500 points pour une image carrée) et le tableau de base.
$img = imagecreatefromjpeg($url);
echo get_main_color($img, 50, $colors);
J’ai fait de nombreux tests bien sur, en voici quelque-uns avec les fonds d’écrans de Windows 7 : http://www.strangeplanet.fr/files/color/
On remarque que sur certaines images Picasa est plus pertinent, sur d’autres c’est ma fonction… on ne peut pas tout avoir !