Hello,

I would like to build (or find) a function that can take RGB values and convert them to their HSB equivelents and vice versa. HSB stands for Hue, Saturation and Brightness (the 'B' is sometimes a 'V' for Value). People with Photoshop experience (and others) will probably know how these work. But for the uninitiated:

Hue represents the spectrum in 360 degrees, from Red through all the colors of the rainbow (color wheel) and back to Red again.

Saturation is the intensity or purity of the color. 100% is totally saturated and pure. 0% is totally grey.

Brightness is the relative lightness or darkness of the color, usually measured as a percentage from 0% (black) to 100% (white).

Although these are the "proper" definitions they aren't easy to verify when using any color sliders in my Adobe software. But regardless...

I want to be able to control the color with one or some of these three factors. For instance, if I have a middle-green which is rgb(70,170,50) or aprox. rgb(27%,67%,19%) and I want to get a lighter shade of that, for instance rgb(190,255,170), is there a way of doing this through any php function, and if not does anyone know the math or could point me towards a resource that has the math.

Another example would be to adjust the Hue by 180 degrees to get the complimentary color.

I know Adobe uses the Lab color model for all their internal color conversions, but I can't see how that would help me here!

    Check out my post in Code Critique. It should be a very good place for you to start... it should be easily modified to take the inputs as you want them.

    <?php
    
    function RGB_to_HSV ( $r , $g , $b )
    {
    	$r = $r/255;
    	$g = $g/255;
    	$b = $b/255;
    
    $MAX = max($r,$g,$b);
    $MIN = min($r,$g,$b);
    
    if     ($MAX == $MIN) return array(0,0,$MAX);
    if     ($r == $MAX) $HUE = ((0 + (($g - $b)/($MAX-$MIN))) * 60);
    elseif ($g == $MAX) $HUE = ((2 + (($b - $r)/($MAX-$MIN))) * 60);
    elseif ($b == $MAX) $HUE = ((4 + (($r - $g)/($MAX-$MIN))) * 60);
    if     ( $HUE < 0 ) $HUE += 360;
    
    return array($HUE,(($MAX - $MIN)/$MAX),$MAX);
    }
    
    function HSV_to_RGB ( $H , $S , $V )
    {	
    	if ($S == 0) return array($V * 255,$V * 255,$V * 255);
    
    $Hi = floor($H/60);
    $f  = (($H/60) - $Hi);
    $p  = ($V * (1 - $S));
    $q  = ($V * (1 - ($S * $f)));
    $t  = ($V * (1 - ($S * (1 - $f))));
    
    switch ( $Hi )
    {
    	case 0  : $red = $V; $gre = $t; $blu = $p; break;
    	case 1  : $red = $q; $gre = $V; $blu = $p; break;
    	case 2  : $red = $p; $gre = $V; $blu = $t; break;
    	case 3  : $red = $p; $gre = $q; $blu = $V; break;
    	case 4  : $red = $t; $gre = $p; $blu = $V; break;
    	case 5  : $red = $V; $gre = $p; $blu = $q; break;
    	default : exit("error -- invalid parameters\n\n");
    }
    
    return array(round($red * 255),round($gre * 255),round($blu * 255));
    }
    
    ?>
    

      Hi Mike,

      Really that is awesome. I have been searching for better ways of handling color in my sites. Presently I have a color manager (.php) that is hooked to the database. And then on each page I assign a color theme. I have made 5 themes for the whole site. Within those themes are the dynamic colors: $light, medium, dark, light_secondary, medium_secondary, dark_secondary and bright each with their own rgb values. The purpose of it all is to create intuitive site "areas". Of course I can change the theme on any page, as the dynamic colors just change with it

      BUT , I woke up wee-hours like, the other morning and I thought man I need to be able to just say -- make a tint of this color or change the hue of that color by so many degrees, or whatever. So the first thing I do is come to phpbuilder! Really, Mike, I can't believe that the someone who actually made a succint couple of functions for the problem would have read my post! Bravo! I am very grateful!

      Well, I am going to "bring it in" to my scripts and try it out. Stay tuned! 🙂

        Glad my functions could be of help to someone else! I had written them for a similar color changing module on my site... basically it would change a set of colors (like yours they were $light, $medium, etc..) based on the current image being displayed in a gallery. Very similar to how the galleries over on Noah Grey's (http://www.noahgrey.com/) show appropriate color backgrounds on the image view page.

        Let me know if you have any other questions about them! 🙂

          Finally got a chance to work with your functions.

          Basically the first thing I did was put a regex into the rgb_to_hsv(), so that I can just feed it a value like rgb(150,200,255) and it will extract the red, green and blue. For example:

          $hsv = rgb_to_hsv("rgb(150,200,255)");

          Verified the regex was working:

          // print_r results:
          // Array ( [0] => rgb(120,200,255) [1] => 120 [2] => 200 [3] => 255 )

          I also changed hsv_to_rgb() to return the value back to a regular rgb value, for instance, rgb(56,94,120)

          Here is the actual line that I changed in your function:

          return "rgb(" . round($red * 255) . "," . round($green * 255) . "," . round($blue * 255) . ")";

          OK, so here's my test page (abbreviated):

          $color = "rgb(120,200,255)";
          $hsv_array = rgb_to_hsv($color);
          $processed_color = hsv_to_rgb($hsv_array);
          print "
          	<body style='background-color: {$processed_color};'>
          		<p>Original Color: {$color}</p>
          		<p>HSV Array: " . print_r($hsv_array,true) . "</p>
          		<p>Processed Color: {$processed_color}</p>
          	</body>
          ";
          

          I expected that the $processed_color would be the same as the original $color. However, here were my results:

          Original Color: rgb(120,200,255)

          HSV Array: Array ( [0] => 204.44444444444 [1] => 0.52941176470588 [2] => 0.47058823529412 )

          Processed Color: rgb(56,94,120)

          As you can see the processed color changed in value from the original. Any ideas?

            Odd indeed.... apparently something is screwy. The conversion to and back again works on my original functions (double checked with Photoshop)...

            Array
            (
                [0] => 204.444444444
                [1] => 0.529411764706
                [2] => 1
            )
            
            Array
            (
                [0] => 120
                [1] => 200
                [2] => 255
            )
            

            Something went screwy in the brightness calculation apparently. You were returned a 0.4xxx value for brightness when it should have returned a full 1. Can I see the fully modified function for rgb_to_hsv?

              function rgb_to_hsv($rgb) { // $red, $green, $blue
              
              // CREDIT: Mike Snead
              
              ereg("^rgb{1}[[.left-parenthesis.]]{1}([0-9]{1,3}){1}[[.comma.]]{1}
              ([0-9]{1,3}){1}[[.comma.]]{1}([0-9]{1,3}){1}[[.right-parenthesis.]]{1}$",
              $rgb, $rgb_array);
              
              // Convert 255-based value to %
              
              $red = $rgb_array[1] / 255;
              $green = $rgb_array[2] / 255;
              $blue = $rgb_array[3] / 255;
              
              // Determine the maximum and minimum values:
              
              $max = max($red, $green, $blue);
              $min = min($red, $green, $blue);
              
              // .. and return 0,0,max, which is the same as a grey%
              
              if($max == $min) {
              	return array(0, 0, $max);
              }
              
              // Determine the Hue value
              
              if($red == $max) {
              	$hue = ((0 + (($green - $blue) / ($max - $min))) * 60);
              } elseif($green == $max) {
              	$hue = ((2 + (($blue - $red) / ($max - $min))) * 60);
              } elseif($blue == $max) {
              	$hue = ((4 + (($red - $green) / ($max - $min))) * 60);
              }
              
              // Adjust the Hue value to correspond to 360 degree color wheel
              
              if($hue < 0) {
              	$hue += 360;
              }
              
              return array($hue, (($max - $min) / $max), $min);
              }
              

                I thought I would also give you the whole page so you could just drop it in and run the test yourself. I guess you will also need the hsv_to_rgb() (revised) function as this returns as "rgb(#,#,#)"

                THE PAGE

                <?php
                include "include/page-class.inc";
                $color = "rgb(120,200,255)";
                $hsv_array = rgb_to_hsv($color);
                $processed_color = hsv_to_rgb($hsv_array);
                ?>
                <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                
                <html xmlns="http://www.w3.org/1999/xhtml">
                
                <head>
                	<title>Color Test</title>
                </head>
                
                <?php
                print "
                	<body style='background-color: {$processed_color};'>
                		<p>Test Page</p>
                		<p>Original Color: {$color}</p>
                		<p>HSV Array: " . print_r($hsv_array,true) . "</p>
                		<p>Processed Color: {$processed_color}</p>
                	</body>
                ";
                ?>
                </html>

                HSV_TO_RGB() (REVISED)

                function hsv_to_rgb($hsv = array()) {// $hue, $saturation, $value
                
                $hue = $hsv[0];
                $saturation = $hsv[1];
                $value = $hsv[2];
                
                if ($saturation == 0) {
                	return "rgb(" . $value * 255 . "," . $value * 255 . "," . $value * 255 . ")";
                	//return array($value * 255, $value * 255, $value * 255);
                }
                
                $hi = floor($hue / 60);
                $f  = (($hue / 60) - $hi);
                $p  = ($value * (1 - $saturation));
                $q  = ($value * (1 - ($saturation * $f)));
                $t  = ($value * (1 - ($saturation * (1 - $f))));
                
                switch ($hi ) {
                	case 0 :  $red = $value; 	$green = $t; 		$blue = $p; 		break;
                	case 1 :  $red = $q; 		$green = $value; 	$blue = $p; 		break;
                	case 2 :  $red = $p; 		$green = $value; 	$blue = $t; 		break;
                	case 3 :  $red = $p; 		$green = $q; 		$blue = $value; 	break;
                	case 4 :  $red = $t; 		$green = $p; 		$blue = $value; 	break;
                	case 5 :  $red = $value; 	$green = $p; 		$blue = $q; 		break;
                	default : exit("Error -- invalid parameters for HSV_to_RGB()\n\n");
                }
                
                return "rgb(" . round($red * 255) . "," . round($green * 255) . "," . round($blue * 255) . ")";
                //return array(round($red * 255), round($green * 255), round($blue * 255));
                } 
                

                  Found it! - The return on rgb_to_hsv() has a mistake. The last value of the array needs to be $max, not min.

                  From:

                  return array($hue, (($max - $min) / $max), $min); 

                  To:

                  return array($hue, (($max - $min) / $max), $max); 

                    Yep, that works perfect now. Original: rgb(120,200,80), Processed : rgb(120,200,80). Really amazing! 🙂

                    OK, now to do some color manipulation. Just to verify:

                    Are these true: Hue is a value from 1 - 360, Saturation is from 0 - 1 (where 0 is the least saturated and 1 is the most), and Value/Brightness is from 0 - 1 (1 being the brightest).

                    (I think I will have more questions, but that's it for now)

                      Yay! Glad it's working now! 🙂

                      Hue is a value from 0 - 360 with 0 being literally the same as 360 as far as the calculations go. Units would be degrees.

                      Saturation is a value from 0 - 1 with 0 meaning the color is gray.

                      Brightness is a value from 0 - 1 with 1 being the brightest (0 will always yield black).

                      Feel free to ask away. Also, I'll try to dig up the page where I fould all the math that I based the algorithm off of. It was a great site for getting the basics of color theory and the differences between the methods of describing a color.

                        Man I'm already having fun here!

                        OK, here are the questions I promised:

                        1. Is there a more sophisticated way of keeping the HUE within the 360 degrees? Here is my code (btw I made the whole thing OOP):

                          if($hue_adjust) {
                          	$this->hsv[0] += (int) $hue_adjust;
                          	if($this->hsv[0] > 360) {
                          		$this->hsv[0] -= 360;
                          	} elseif($this->hsv[0] < 0) {
                          		$this->hsv[0] += 360;
                          	}
                          	$changed = true;
                          }
                          
                        2. This one isn't working. No matter what value I put into the color adjustment parameters (.1, .25, -1, -.5) no change takes place:

                          if($saturation_adjust) {
                          	$this->hsv[1] += (float) $saturation_adjust;
                          	if($this->hsv[1] < 0) {
                          		$this->hsv[1] = 0;
                          	} elseif($this->hsv[1] > 1) {
                          		$this->hsv[1] = 1;
                          	}
                          	$change = true;
                          }
                          

                          (also, I guess the same question applies to the second code block: is there a more sophisticated way of keeping the value from 0 - 1?)

                          I AM SUCH A GEEK.

                          $change = true;

                          S/B

                          $changed = true;

                          :rolleyes:

                          (anyways my other questions still apply...)

                            lol... we all make mistakes like that when we've been staring at the same code for a couple hours... you brain just starts to ignore the little things :p

                            As for keeping the params within the respective limits, well I can't think right now of a more 'sophisticated' way. It appears to be the way that even Buzzley's color picker program keeps his values in range. Which by the way I suggesting giving his uber script a look at... it's very cool!

                            http://www.ultimatespin.com/colors/colorpicker.php

                              Write a Reply...