edainworks.com :: VGR :: extracting the three most used colours of an image, optionally reducing the colours count to fit a smaller palette.

This is the image we run against : demo image
Run :

RQuadling's code

Starting to read image

page generated in 17.037 ms
Starting to sort colours

page generated in 3.995 ms
37438 colours found.
Starting to get 3 most represented colours
00FFFEFF = rgb(255,254,255) occurs 136
00FFFFFF = rgb(255,255,255) occurs 135
00FEFFFF = rgb(254,255,255) occurs 93

page generated in 0.007 ms

VGR's code

Starting to read image
not reducing colours range, nbcol (=256/diviser) = 256, diviser=1

page generated in 14.362 ms
No sort on colours
Starting to get 3 most represented colours (cMax=16843008)
first maximum occuring value is : 00FFFEFF = rgb(255,254,255)( 136 times)
second maximum occuring value is : 00FFFFFF = rgb(255,255,255)( 135 times)
third maximum occuring value is : 00FEFFFF = rgb(254,255,255)( 93 times)


page generated in 294.925 ms

RQuadling's code with reducing the colours count by 8

Starting to read image

page generated in 29.042 ms
Starting to sort colours

page generated in 0.855 ms
8435 colours found.
Starting to get 3 most represented colours
00FCFCFC = rgb(252,252,252) occurs 605
00544C58 = rgb( 84, 76, 88) occurs 361
00544C54 = rgb( 84, 76, 84) occurs 351

page generated in 0.006 ms

VGR's code with reducing the colours count by 2

Starting to read image
reducing colours range, nbcol (=256/diviser) = 128, diviser=2

page generated in 28.356 ms
No sort on colours
Starting to get 3 most represented colours (cMax=8421504)
first maximum occuring value is : 00544C58 = rgb( 84, 76, 88)( 102 times)
second maximum occuring value is : 00504854 = rgb( 80, 72, 84)( 92 times)
third maximum occuring value is : 00524850 = rgb( 82, 72, 80)( 90 times)


page generated in 172.856 ms

Code :
//
// --------------------------- below is RQuadling's code, with some traces added
//
echo"<h2>RQuadling's code</h2>"; //VGR22082009 ADDed

echo 'Starting to read image<br>';
TimerStart();

// Try and open the image.
$r_image = @imagecreatefromjpeg($s_image_filename);

// If the image was opened successfully then continue.
if ($r_image)
   {
   // Get the images dimensions.
   $am_image_data = getimagesize($s_image_filename);

   // Prepare an empty array to store the count.
   $ai_colour_count = array();

   // Iterate the x and y of the image.
   for ($i_x = 0 ; $i_x < $am_image_data[0] ; $i_x++)
      {
      for ($i_y = 0 ; $i_y < $am_image_data[1] ; $i_y++)
         {
         // Get the colour or palette number at x,y.
         $i_rgb = imagecolorat($r_image, $i_x, $i_y);

         // Determine if the image is true colour or paletted.
         if (!imageistruecolor($r_image))
            {
            // Convert the palette index into a real colour.
            $a_rgb = imagecolorsforindex($r_image, $i_rgb);
            $i_rgb = ((($a_rgb['red'] * 256) + $a_rgb['green']) * 256) + $a_rgb['blue'];
            }
//echo "old value=$i_rgb<br>";
//echo (($i_rgb & 0xff0000) >> 16 ).', '. (($i_rgb & 0xff00) >> 8).', '.($i_rgb & 0xff).'<br>';
         if ($REDUCERANGE) { // hash colour
           if (!imageistruecolor($r_image)) {
             $tt1=floor($a_rgb['red'] / 4)*4;
             $tt2=floor($a_rgb['green'] / 4)*4;
             $tt3=floor($a_rgb['blue'] / 4)*4;
             $newvalue=((($tt1 * 256) + $tt2) * 256) + $tt3;
//echo "$tt1 $tt2 $tt3<br>";
           } else {
//echo "rgb=$i_rgb<br>";
             $courant=$i_rgb%256; $reste=($i_rgb-$courant)/256;
             $tt3=floor($courant / 4)*4;
             $courant=$reste%256; $reste=($reste-$courant)/256;
             $tt2=floor($courant / 4)*4;
             $courant=$reste%256; $reste=($reste-$courant)/256;
             $tt1=floor($courant / 4)*4;
//echo "$tt1 $tt2 $tt3 (reste=$reste)<br>";
             $newvalue=((($tt1 * 256) + $tt2) * 256) + $tt3;
           }
           $i_rgb=$newvalue;
         }
//echo "new value=$i_rgb<br>";
         
         // Update the count array with the colour.
         if (!isset($ai_colour_count[$i_rgb]))
            {
            $ai_colour_count[$i_rgb] = 0;
            }
         ++$ai_colour_count[$i_rgb];
         }
      }

   TimerStop(TRUE);
   echo 'Starting to sort colours<br>';
   TimerStart();
   // Sort the colour map preserving the colour/palette
   arsort($ai_colour_count);
   TimerStop(TRUE);
   echo count($ai_colour_count).' colours found.<br>';
   echo 'Starting to get 3 most represented colours<br>';
   TimerStart();
   
   // Determine the number of colours to report.
   $i_colours_to_report = 3;

   // Reset the array to the beginning.
   reset($ai_colour_count);
   
   // Report the colours.
   while($i_colours_to_report > 0)
      {
      $i_rgb = key($ai_colour_count);
      $i_count = current($ai_colour_count);
      echo sprintf("%08X = rgb(%3d,%3d,%3d) occurs %8d{$eoln}", $i_rgb, ($i_rgb & 0xff0000) >> 16, ($i_rgb & 0xff00) >> 8, ($i_rgb & 0xff), $i_count);
      next($ai_colour_count);
      --$i_colours_to_report;
      }
   TimerStop(TRUE);
   }
else
   {
   die('Could not open : ' . $s_image_filename);
   }

//
// --------------------------- below is VGR's code again, to demonstrate colours count reduction
//

$REDUCERANGE=TRUE;
$diviser=2; // 1 = 256 colours (232 in web palette) ; 8 = 32 colours (ugly)

echo '<hr>Starting to read image<br>';
TimerStart();

// Try and open the image.
$r_image=@imagecreatefromjpeg($s_image_filename);

// If the image was opened successfully then continue.
if ($r_image) {
  // Get the images dimensions and colour depth
  $am_image_data=getimagesize($s_image_filename);
  // Determine if the image is true colour or paletted.
  $istruecolour=imageistruecolor($r_image);
  $nbcol=256/$diviser;
  echo (($REDUCERANGE)?'reducing colours range':'not reducing colours range').", nbcol (=256/diviser) = $nbcol, diviser=$diviser<br>";
  
  // Prepare an empty array to store the count.
  $ai_colour_count = array();
  // Iterate the x and y of the image.
  for ($i_x = 0 ; $i_x < $am_image_data[0] ; $i_x++) {
    for ($i_y = 0 ; $i_y < $am_image_data[1] ; $i_y++) {
      // Get the colour or palette number at x,y.
      $i_rgb = imagecolorat($r_image, $i_x, $i_y);
      if (!$istruecolour) {
        // Convert the palette index into a real colour.
        $a_rgb = imagecolorsforindex($r_image, $i_rgb);
        $i_rgb = ((($a_rgb['red'] * 256) + $a_rgb['green']) * 256) + $a_rgb['blue'];
      }
      if ($REDUCERANGE) { // hash colour
        if (!$istruecolour) { //VGR22082009 FIXed stupid réaffectation in test
          $tt1=floor($a_rgb['red'] / $diviser)*$diviser;
          $tt2=floor($a_rgb['green'] / $diviser)*$diviser;
          $tt3=floor($a_rgb['blue'] / $diviser)*$diviser;
          $newvalue=((($tt1 * 256) + $tt2) * 256) + $tt3;
          //test echo "not true colour : value $i_rgb becomes $newvalue<br>";
        } else {
          $courant=$i_rgb%256; $reste=($i_rgb-$courant)/256;
          $tt3=floor($courant / $diviser)*$diviser;
          $courant=$reste%256; $reste=($reste-$courant)/256;
          $tt2=floor($courant / $diviser)*$diviser;
          $courant=$reste%256; $reste=($reste-$courant)/256;
          $tt1=floor($courant / $diviser)*$diviser;
          $newvalue=((($tt1 * 256) + $tt2) * 256) + $tt3;
          //test echo "true colour : value $i_rgb becomes $newvalue<br>";
        }
        $i_rgb=$newvalue;
      }
      // Update the count array with the colour.
      @$ai_colour_count[$i_rgb]++;
    }
  }

  TimerStop(TRUE);
  echo 'No sort on colours<br>';
  TimerStart();
  $cMax=(($nbcol*256+$nbcol)*256+$nbcol);
  echo "Starting to get 3 most represented colours (cMax=$cMax)<br>";
  // display results of most-occuring value
  $locMax=0; $locMax2=0; $locMax3=0;
  $locMaxI2=0; $locMaxI=0;
  for ($i=0;$i<=$cMax;$i++) {
//if ($i%100000==0) echo "$i<br>";
    //VGR22082009 MOD enhanced loop, was very slow [from 1min22s to 3s] : if (@$ai_colour_count[$i]>$locMax) { 
    if (isset($ai_colour_count[$i]))
     if (@$ai_colour_count[$i]>$locMax) {
       $locMaxI3=$locMaxI2; $locMaxI2=$locMaxI; $locMaxI=$i;
       $locMax3=$locMax2; $locMax2=$locMax; $locMax=$ai_colour_count[$i];
     } else if (@$ai_colour_count[$i]>$locMax2) { 
       $locMaxI3=$locMaxI2; $locMaxI2=$i;
       $locMax3=$locMax2; $locMax2=$ai_colour_count[$i];
     } else if (@$ai_colour_count[$i]>$locMax3) { 
       $locMaxI3=$i;
       $locMax3=$ai_colour_count[$i];
     }
    // else NOP, loop
  }
  //VGR22082009 MOD to display hex & rgb values 
  echo "first maximum occuring value is : ";
  echo sprintf("%08X = rgb(%3d,%3d,%3d)", $locMaxI, ($locMaxI & 0xff0000) >> 16, ($locMaxI & 0xff00) >> 8, ($locMaxI & 0xff));
  echo "( $locMax times)<br>";
  echo "second maximum occuring value is : ";
  echo sprintf("%08X = rgb(%3d,%3d,%3d)", $locMaxI2, ($locMaxI2 & 0xff0000) >> 16, ($locMaxI2 & 0xff00) >> 8, ($locMaxI2 & 0xff));
  echo "( $locMax2 times)<br>";
  echo "third maximum occuring value is : ";
  echo sprintf("%08X = rgb(%3d,%3d,%3d)", $locMaxI3, ($locMaxI3 & 0xff0000) >> 16, ($locMaxI3 & 0xff00) >> 8, ($locMaxI3 & 0xff));
  echo "( $locMax3 times)<br>";
  echo '<br>'; 
  TimerStop(TRUE);
} else die("Could not open : $s_image_filename");

9. Conclusion top

FR

Ici, je comparais du code de RQuadling au mien pour l'extraction des 3 couleurs les plus utilisées d'une image. La différence principale tenait à la méthode de tri.
Maintenant que l'implémentation de asort() et al dans PHP a été améliorée, la solution utilisant ces fonctions est plus rapide que la mienne, à base de "tri instantané".

Néanmoins, mon code permet aussi, sur option, de réduire le nombre de couleurs de l'image "au mieux" pour s'accomoder d'une palette réduite.
Notez bien que théoriquement les deux solutions réduident le nombre de couleurs, mais qu'il ne me souvient plus de si la solution de RQuadling y réussit ;-)

Cordialement,

EN

In here, was comparing RQuadling's code to mine to extract the three most used colours of an image.
Now, asort() has been optimized in PHP so the first solution is faster than the second.

This said, my solution also enables to reduce the colours count to fit a reduced palette.
Note that both solutions theoretically reduce the colours count, but I don't remember if RQuadling's works out nicely ;-)

Best regards,

Vincent Graux (VGR) for European Experts Exchange and Edaìn Works  back to list of test scripts
Last update 2024-05-13 15:34:03