1

I'm building a tool where i pick the dominant colors from an image and i have done that bit pretty well, the problem i'm facing now is how to combine the returned colors into a broad palette range like the X11 color range http://en.wikipedia.org/wiki/Web_colors#X11_color_names

So for example if i got : Color RGB:rgb(102,102,153), i would want to chalk it up to the purple colors and rgb(51,102,204) to blue and so on and so forth. Now i really can't figure out how to do this. Is there a library or something i can use or how should i code it? I'm using imagemagick and php btw.

Is it possible to generate an array containing the range of rgb for each base colors and then see if my new color lies in it?

Thanks in advance guys!

Suyash
  • 625
  • 1
  • 5
  • 22
  • You could also see this problem like which intersection of the spectrum does the color lie in? http://upload.wikimedia.org/wikipedia/commons/thumb/c/c2/AdditiveColor.svg/200px-AdditiveColor.svg.png – Suyash Jul 20 '12 at 22:21

2 Answers2

1

I've been looking for exactly the same thing for an upcoming project, though I'm happy with just the basic rainbow colours.

Searching around, the best way seems to be to convert your RGB colour to HSL. The (H)ue part is very useful to see what broad part of the rainbow the colour lies in. Then add a couple of extra bits to capture black, white, grey and brown.

Here is my PHP code with a reference to the RGB -> HSL convert routine. I'm sure it could be optimized a bit better but it's a start!

Obviously colour is quite subjective so you may want to play around with the values for each colour range - just use one of the online colour picker utilities or even the Windows colour picker.

<?php

  function RGB_TO_HSV ($R, $G, $B) { // RGB Values:Number 0-255
                                     // HSV Results:Number 0-1
  // this function from http://stackoverflow.com/questions/1773698/rgb-to-hsv-in-php
  $HSL = array();

  $var_R = ($R / 255);
  $var_G = ($G / 255);
  $var_B = ($B / 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 = ( ( ( $max - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
     $del_G = ( ( ( $max - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
     $del_B = ( ( ( $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++;
     if (H>1) $H--;
  }

  $HSL['H'] = $H;
  $HSL['S'] = $S;
  $HSL['V'] = $V;
  return $HSL;
}

// convert an RGB colour to HSL
$hsl = RGB_TO_HSV(51,102,204);       // rgb values 0-255

$hue = round($hsl['H'] * 255, 0);   // round hue from 0 to 255 for ease of use
$sat = $hsl['S'];                     // 0 to 1
$val = $hsl['V'];                     // 0 to 1

$colour = "Red";                     // default to red

if ($hue >= 10 && $hue <= 35) {
   $colour = "Orange";
   if ($val < 0.69) $colour = "Brown";
}
if ($hue >= 36 && $hue <= 44) $colour = "Yellow";
if ($hue >= 45 && $hue <= 107) $colour = "Green";
if ($hue >= 108 && $hue <= 182) $colour = "Blue";
if ($hue >= 183 && $hue <= 206) $colour = "Purple";
if ($hue >= 207 && $hue <= 245) $colour = "Pink";
if ($val < 0.1) $colour = "Black";
if ($val > 0.9) $colour = "White";
if ($sat < 0.105) $colour = "Grey";


// show the result
echo $colour;

?>
raymortim
  • 26
  • 1
  • Hey! Thanks a lot for showing me a new approach the problem, and i'm fine with rainbow colors too but the current ranges you have implemented above are a little faulty, like 102,102,153 should be purple but this shows blue. Also why did you round off hue in the example? the online palette that i took shows 0-360 for hue. Can you please tell me how and from where can i pick up industry standard categorization of these ranges? – Suyash Jul 23 '12 at 08:27
  • also u defined the hue to lie between 0-255 but i'm getting negative values as well. – Suyash Jul 23 '12 at 10:08
  • The hue is converted to 0-255 to match the colour picker I was using though the normal range does seem to be 0-360. It did seem quite accurate in all the tests I did. I don't know why you're getting negative values. – raymortim Jul 23 '12 at 12:51
  • the $ is missing in lines: if (H<0) $H++; if (H>1) $H--; hence the negative values are not returning properly. and yeah i'm using another dataset to create a more accurate computation. Thanks for all the help tho! really appreciate! :) – Suyash Jul 23 '12 at 13:58
0

OK, forget the previous code, here is a much more reliable version I put together from several sources which also uses the more common 0-360 for hue.

I also added a test little routine that picks random colours then 'tells' you what they are!

Seems more reliable, some fiddling might be needed around the cut-off values (especially orange/grey etc). I used this online colour picker for testing.

<?php

function RGBtoHSL($r,$g,$b) {  
// based on code and formulas from:
// http://www.had2know.com/technology/hsl-rgb-color-converter.html
// http://colorgrader.net/index.php/dictionary-a-tutorials/color-theory/93-math-behind-colorspace-conversions-rgb-hsl.html
// http://proto.layer51.com/d.aspx?f=1135


$max = max($r, $g, $b);
$min = min($r, $g, $b);
$d = ($max - $min) / 255;
$lum = round((($max+$min)/2)/255, 2);

$sat = 0;
if ($lum > 0) $sat = round($d/(1 - (2*$lum-1)), 2);

$hue = 0;  
if(($r==$g) && ($g==$b))  $hue = 0;  
else if($r>=$g && $g>=$b) $hue = 60*($g-$b)/($r-$b);  
else if($g>=$r && $r>=$b) $hue = 60  + 60*($g-$r)/($g-$b);  
else if($g>=$b && $b>=$r) $hue = 120 + 60*($b-$r)/($g-$r);  
else if($b>=$g && $g>=$r) $hue = 180 + 60*($b-$g)/($b-$r);  
else if($b>=$r && $r>=$g) $hue = 240 + 60*($r-$g)/($b-$g);  
else if($r>=$b && $b>=$g) $hue = 300 + 60*($r-$b)/($r-$g);  
else $hue = 0;  
$hue = round($hue, 0);  
$hsl = array();
$hsl['h'] = $hue;
$hsl['s'] = $sat;
$hsl['l'] = $lum;
return $hsl;
}  




// example: pick 42 random colours then identify them
echo "<div style='float: left; width: 1000px;'>";

srand;
for ($f=0; $f < 42; $f++) {

$red = rand(0, 255); 
$green = rand(0, 255); 
$blue = rand(0, 255); 

$hsl = RGBtoHSL($red, $green, $blue);

$hue = $hsl['h'];
$sat = $hsl['s'];
$lum = $hsl['l'];

$colour = "Red";            // default to red

if ($hue >= 11 && $hue <= 45) {
$colour = "Orange";
if ($lum < 0.51) $colour = "Brown";
}
if ($hue >= 46 && $hue <= 62) $colour = "Yellow";
if ($hue >= 63 && $hue <= 160) $colour = "Green";
if ($hue >= 161 && $hue <= 262) $colour = "Blue";
if ($hue >= 263 && $hue <= 292) $colour = "Purple";
if ($hue >= 293 && $hue <= 349) $colour = "Pink";
if ($sat < 0.07) $colour = "Grey";  // do grey before black/white
if ($lum < 0.10) $colour = "Black";
if ($lum > 0.97) $colour = "White";

echo "<div style='float: left; width: 150px; border: 1px solid #000000; margin: 5px;'>";      
echo "<div style='float: left; width: 20px; height: 120px; background-color: rgb($red, $green, $blue); margin-right: 10px;'></div>";
echo "<p style='width: 33%; float: left;'>R=$red<br/>G=$green<br/>B=$blue</p>";
echo "<p style='width: 33%; float: right;'>H=$hue<br/>S=$sat<br/>L=$lum</p>";
echo "<p><b>$colour</b></p>";
echo "</div>";
}

echo "</div>";

?>
raymortim
  • 26
  • 1
  • respect for the hardwork! i however took your last example and have been fiddling with the code adding tons of more variations with luminosity and saturation cos boundary line values like 60 and 160 and 240 get on both sides of the court. Will post my code once done. – Suyash Jul 23 '12 at 21:19