# RGB to HSV color space conversion (C)

The RGB color space, used directly by most computer devices, expresses colors as an additive combination of three additive primary colors of light: red, green, and blue. A commonly used color space that corresponds more naturally to human perception is the HSV color space, whose three components are hue, saturation, and value. The formulas used to convert RGB to HSV depend on which of the RGB components is largest and which is smallest, and are described at *Transformation from RGB to HSV*.

## Contents |

## [edit] Interfaces and preparation

In our first implementation, designed for ease of understanding and accuracy, we use high-precision floating-point operations. The red, green, and blue channels are each specified by values between 0 and 1. The hue is specified by an angle in degrees between 0 and 360, and both saturation and value are specified by values between 0 and 1. Our function will receive a structure as input and return a structure as output:

<<color structure>>=structrgb_color{doubler, g, b; /* Channel intensities between 0.0 and 1.0 */};structhsv_color{doublehue; /* Hue degree between 0.0 and 360.0 */doublesat; /* Saturation between 0.0 (gray) and 1.0 */doubleval; /* Value between 0.0 (black) and 1.0 */};<<RGB to HSV conversion function>>=structhsv_color rgb_to_hsv(structrgb_color rgb){perform conversion}

We will need to know the largest and smallest of the three RGB values:

<<find min and max RGB value>>=doublergb_min, rgb_max; rgb_min = MIN3(rgb.r, rgb.g, rgb.b); rgb_max = MAX3(rgb.r, rgb.g, rgb.b);

The macros MIN3 and MAX3 find the minimum or maximum of three values and are defined as:

<<macros>>=#define MIN3(x,y,z) ((y) <= (z) ? \((x) <= (y) ? (x) : (y)) \ : \ ((x) <= (z) ? (x) : (z)))#define MAX3(x,y,z) ((y) >= (z) ? \((x) >= (y) ? (x) : (y)) \ : \ ((x) >= (z) ? (x) : (z)))

## [edit] Converting to HSV

We will compute the values in the following order:

<<perform conversion>>=structhsv_color hsv; find min and max RGB value compute value normalize value to 1 compute saturation normalize saturation to 1 compute huereturnhsv;

The value (V in HSV) is the easiest component to compute, simply being `rgb_max`

. If the value is zero, the color is black and we exit, arbitrarily assigning values to the other channels:

<<compute value>>=hsv.val = rgb_max;if(hsv.val == 0){hsv.hue = hsv.sat = 0;returnhsv;}

Next, to simplify further calculations we divide all three channels by the HSV value to scale the color up to a color of value 1:

<<normalize value to 1>>=/* Normalize value to 1 */ rgb.r /= hsv.val; rgb.g /= hsv.val; rgb.b /= hsv.val; rgb_min = MIN3(rgb.r, rgb.g, rgb.b); rgb_max = MAX3(rgb.r, rgb.g, rgb.b);

Division by zero is avoided because we would have returned if `hsv.val`

= 0.

The saturation (S in HSV) is controlled by how widely separated the RGB values are. When the values are close together, the color will be close to gray. When they're far apart, the color will be more intense or pure:

<<compute saturation>>=hsv.sat = rgb_max - rgb_min;if(hsv.sat == 0){hsv.hue = 0;returnhsv;}

If the saturation is zero the color is gray and we arbitrarily assign zero to hue and return.

We now normalize the color to give it both a value and saturation of 1:

<<normalize saturation to 1>>=/* Normalize saturation to 1 */ rgb.r = (rgb.r - rgb_min)/(rgb_max - rgb_min); rgb.g = (rgb.g - rgb_min)/(rgb_max - rgb_min); rgb.b = (rgb.b - rgb_min)/(rgb_max - rgb_min); rgb_min = MIN3(rgb.r, rgb.g, rgb.b); rgb_max = MAX3(rgb.r, rgb.g, rgb.b);

Finally, hue, which determines whether the color is red, blue, green, yellow, and so on, is the most complex to compute. Red is at 0 degrees, green is at 120 degrees, and blue is at 240 degrees. The maximum RGB color controls our starting point, and the difference of the other two colors determines how far we move away from it, up to 60 degrees away (halfway to the next primary color):

<<compute hue>>=/* Compute hue */if(rgb_max == rgb.r){hsv.hue = 0.0 + 60.0*(rgb.g - rgb.b);if(hsv.hue < 0.0){hsv.hue += 360.0;}}elseif(rgb_max == rgb.g){hsv.hue = 120.0 + 60.0*(rgb.b - rgb.r);}else/* rgb_max == rgb.b */{hsv.hue = 240.0 + 60.0*(rgb.r - rgb.g);}

We always subtract in the direction that causes the hue to move toward the larger of the two smaller channel values. Note that moving down from red requires handling of wraparound to ensure that we keep the angle in the 0 to 360 range; this isn't necessary, but the range would be -60 degrees to 300 degrees without it.

## [edit] Sample code

We can demonstrate the algorithm with this small sample program that takes three RGB components on the command line and outputs the HSV components:

<<rgb_to_hsv.c>>=#include <stdlib.h>#include <stdio.h>macros color structure RGB to HSV conversion functionintmain(intargc,char* argv[]){structrgb_color rgb;structhsv_color hsv; rgb.r = atof(argv[1]); rgb.g = atof(argv[2]); rgb.b = atof(argv[3]); hsv = rgb_to_hsv(rgb); printf("Hue: %f\nSaturation: %f\nValue: %f\n\n", hsv.hue, hsv.sat, hsv.val);return0;}

Here's a sample invocation corresponding to the light greyish-blue color shown to the right below:

$ ./rgb_to_hsv 0.4392157 0.6745098 0.71372549 Hue: 188.571430 Saturation: 0.384615 Value: 0.713725

## [edit] Efficient integer version

When converting many colors, for example during certain image format conversions where this needs to be done for each pixel, an optimized implementation is useful. Much of the algorithm is the same, except that we work primarily with integers and we avoid the normalization steps by folding them into subsequent computation. Note that unlike the first version, which is always precise enough to convert back to the same 24-bit RGB color, this one can lose some information.

First, here are our data structures, modified to use integers:

<<integer color structure>>=structrgb_color{unsignedcharr, g, b; /* Channel intensities between 0 and 255 */};structhsv_color{unsignedcharhue; /* Hue degree between 0 and 255 */unsignedcharsat; /* Saturation between 0 (gray) and 255 */unsignedcharval; /* Value between 0 (black) and 255 */};

The type of rgb_min and rgb_max similarly changes:

<<integer find min and max RGB value>>=unsignedcharrgb_min, rgb_max; rgb_min = MIN3(rgb.r, rgb.g, rgb.b); rgb_max = MAX3(rgb.r, rgb.g, rgb.b);

The value is calculated the same way as before, but instead of normalizing the value to 1, we divide by the value when computing saturation:

<<integer compute saturation>>=hsv.sat = 255*long(rgb_max - rgb_min)/hsv.val;if(hsv.sat == 0){hsv.hue = 0;returnhsv;}

Note that we also have to scale up by 255, since a quotient of values in the range 0 to 255 will be in the range 0 to 1, and we want our answer in the range 0 to 255. We have to multiply before dividing to avoid truncation.

Finally, we fold the normalization of saturation into the hue computation by dividing the differences by `rgb_max - rgb_min`

:

<<integer compute hue>>=/* Compute hue */if(rgb_max == rgb.r){hsv.hue = 0 + 43*(rgb.g - rgb.b)/(rgb_max - rgb_min);}elseif(rgb_max == rgb.g){hsv.hue = 85 + 43*(rgb.b - rgb.r)/(rgb_max - rgb_min);}else/* rgb_max == rgb.b */{hsv.hue = 171 + 43*(rgb.r - rgb.g)/(rgb_max - rgb_min);}

Note that, to fit the hues in the 0 to 255 range, we've moved green to 85 and blue to 171, and changed the 60 factor to 43. We no longer need to check if hue is less than zero; C's wraparound semantics do just what we want. There is also no need to adjust for the value normalization, since we're taking quotients which produce fractions between 0 and 1.

The overall conversion function now looks like this:

<<integer RGB to HSV conversion function>>=structhsv_color rgb_to_hsv(structrgb_color rgb){structhsv_color hsv; integer find min and max RGB value compute value integer compute saturation integer compute huereturnhsv;}

Here's the sample program for this version:

<<rgb_to_hsv_int.c>>=#include <stdlib.h>#include <stdio.h>macros integer color structure integer RGB to HSV conversion functionintmain(intargc,char* argv[]){structrgb_color rgb;structhsv_color hsv; rgb.r = (unsignedchar)atoi(argv[1]); rgb.g = (unsignedchar)atoi(argv[2]); rgb.b = (unsignedchar)atoi(argv[3]); hsv = rgb_to_hsv(rgb); printf("Hue: %d\nSaturation: %d\nValue: %d\n\n", hsv.hue, hsv.sat, hsv.val);return0;}

$ ./rgb_to_hsv 112 172 182 Hue: 135 Saturation: 98 Value: 182

Download code |