Couleurs dominantes d'une image en php
samedi 5 avril 2008 à 19:11, Catégorie Developpement (#2)
Voici la problématique : connaître la ou les couleurs dominantes. Intuitivement on se dit que ça correspond à la couleur "moyenne"...
Mais qu'en est-il vraiment ?
Je vous propose de suivre avec moi mes réflexions et mes tentatives pour trouver l'algo-qui-va-bien.
Première tentative : la couleur d'une image réduite en 1x1 pixel
$filename = "mon_image.jpg";
$img = imagecreatefromjpeg($filename);
list($w, $h) = getimagesize($filename);
$img2 = imagecreatetruecolor(1, 1);
imagecopyresampled($img2, $img, 0, 0, 0, 0, 1, 1, $w, $h);
imagedestroy($img);
$color = imagecolorat($img2, 0, 0);
$result = imagecolorsforindex($img2, $color);
imagedestroy($img2);
Ici, $result nous donne donne donc la couleur (en RGB) du seul pixel de notre nouvelle image. La couleur est légèrement différente avec imagecopyresize.
Cela peut nous donner une idée sur plusieurs couleurs (il suffit de redimensionner l'image en 3x3 pour avoir les 9 couleurs "moyennes") mais ici, pour une seule couleur cela nous donne très souvent une couleur grisâtre...
Seconde tentative : la couleur moyenne de toutes les couleurs d'une image
Issue des commentaires de la fonction imagecolorat.
$filename = "mon_image.jpg";
$img = imagecreatefromjpeg($filename);
list($w, $h) = getimagesize($filename);
$r = $g = $b = 0;
for($y = 0; $y < $h; $y++) {
for($x = 0; $x < $w; $x++) {
$rgb = imagecolorat($img, $x, $y);
$r += $rgb >> 16;
$g += $rgb >> 8 &255;
$b += $rgb &255;
}
}
$pxls = $w * $h;
$r = dechex(round($r / $pxls));
$g = dechex(round($g / $pxls));
$b = dechex(round($b / $pxls));
if (strlen($r) < 2) {
$r = 0 . $r;
}
if (strlen($g) < 2) {
$g = 0 . $g;
}
if (strlen($b) < 2) {
$b = 0 . $b;
}
imagedestroy($img);
$result = "#" . $r . $g . $b;
Troisième tentative : on pixelise une image
Voir la fonction pixelate de http://www.hudzilla.org/phpbook/read.php/11_2_24.
Il faut ensuite prendre la couleur des pixels ainsi créés.
Grosso modo on se retrouve avec les mêmes problèmes que la première tentative.
Quatrième tentative : on transforme une image en vraies couleurs en image à palette
On transforme en l'image en image à palette et pour chaque index de la palette, on affiche sa couleur.
$filename = "mon_image.jpg";
$nbColor = 9;
$img = imagecreatefromjpeg($filename);
imagetruecolortopalette($img , true, $nbColor);
$cc = imagecolorstotal($img );
for($n = 0; $n < $cc; $n++) {
$c = imagecolorsforindex($img , $n);
$result[] = sprintf('#%02X%02X%02X', $c['red'], $c['green'], $c['blue']);
}
imagedestroy($img);
En réduisant les couleurs d'une image à palette de 9 couleurs, on peut se dire que seules les 9 couleurs dominantes restent. Sauf que dans la palette il y a systématiquement du blanc. Curieux. Je n'ai pas vraiment réussi à comprendre pourquoi une couleur blanche était toujours présente.
En plus si on indique 9 couleurs pour l'image à palette, on en a généralement moins. Il faut donc en faire une fonction récursive avec pour "condition stop" le fait que le nombre de couleurs réelles dans la palette correspond au nombre voulu...
Tentative finale : on "arrondi" les couleurs et on compte leur présence
On comptabilise le nombre de pixels présents dans l'image pour chacune des couleurs converti en websafe. Au final, on obtient un tableau représentatif des dominances par couleur.
function colorPalette($imgname)
{
$im = imagecreatefromjpeg($imgname);
$width = imagesx($im);
$height = imagesy($im);
$palette = array();
for ($j = 0;$j < $height;$j++) {
for ($i = 0;$i < $width;$i++) {
$color = imagecolorat($im, $i, $j);
$r = ($color >> 16) &0xFF;
$g = ($color >> 8) &0xFF;
$b = $color &0xFF;
$rgbtohex = dechex($color);
$websafe = hex2websafe($rgbtohex);
if (array_key_exists($websafe, $palette)) {
$palette[$websafe]++;
} else {
$palette[$websafe] = 1;
}
}
}
arsort($palette);
imagedestroy($im);
return $palette;
}
function hex2websafe($hex)
{
// websafe color : only "00", "33", "66", "99", "CC" or "FF"
$step= 51;
$result = "";
$rgb['r'] = hexdec(substr($hex, 0, 2));
$rgb['g'] = hexdec(substr($hex, 2, 2));
$rgb['b'] = hexdec(substr($hex, 4, 2));
foreach($rgb as $color) {
$websafe = $step * round($color / $step);
$result .= str_pad(dechex($websafe), 2, '0', STR_PAD_LEFT);
}
return $result;
}
function limitedPalette($palette, $length)
{
$n = 0;
$result = array();
foreach ($palette as $key => $values) {
$result[$key] = $values;
$n++;
if ($n >= $length) break;
}
return $result;
}
$filename = "pangolin.jpg";
//On chope toutes les couleurs de l'image
$allColors = colorPalette("$filename");
//on prend seulement les 8 premières couleurs
$limitedColors = limitedPalette($allColors, 8);
//on affiche les 8 couleurs dominantes de l'image
echo "<p><img src=".$filename." /></p>";
foreach ($limitedColors as $color => $infos) {
echo "<div style='background-color:#$color;width:100px;height:100px;'>#$color</div>";
}
Tout cela nous donne :
Conclusion
Au terme d'une lutte acharnée avec mon image de pangolin, la palette finale des 8 couleurs représente assez bien les couleurs dominantes de cette image.
Si l'on veut écarter les nuances de couleurs (ici nous avons des bleus relativement proche) il faut allez plus loin qu'un simple "arrondissement" des couleurs en websafe. Pour cela on peut modifier le pas (51 (ou 0x33) pour le websafe, un nombre supérieur pour un "arrondissement" plus prononcé). Attention aux effets de bords cependant...
Amateur de Coca-Vanille et des loutres aquatiques, Sylvain DELEZIR aime faire des kikoolol avec les kiwis 2.0.
Vos réactions
Suivre les réactions à ce billet via rss.
Le jeudi 20 novembre 2008 à 17:38, par axinit :: #
Le vendredi 21 novembre 2008 à 11:53, par SylvainD :: email :: site :: #
Le vendredi 23 octobre 2009 à 15:03, par vigrx :: email :: site :: #
Le dimanche 13 décembre 2009 à 22:05, par CealAlomaZoof :: email :: site :: #
Le dimanche 13 décembre 2009 à 22:06, par CealAlomaZoof :: email :: site :: #
Le dimanche 13 décembre 2009 à 22:06, par CealAlomaZoof :: email :: site :: #
Le dimanche 13 décembre 2009 à 22:06, par CealAlomaZoof :: email :: site :: #
Le dimanche 13 décembre 2009 à 22:06, par CealAlomaZoof :: email :: site :: #
Le dimanche 13 décembre 2009 à 22:06, par CealAlomaZoof :: email :: site :: #
Le dimanche 13 décembre 2009 à 22:06, par CealAlomaZoof :: email :: site :: #
Le dimanche 13 décembre 2009 à 22:06, par CealAlomaZoof :: email :: site :: #
Le mardi 22 décembre 2009 à 08:37, par tedy :: email :: site :: #
Le jeudi 31 décembre 2009 à 12:01, par SantaBlue :: email :: site :: #
Le dimanche 3 janvier 2010 à 06:52, par tedy :: email :: site :: #
Ajouter un commentaire