3 min read

Image HSV: RBG for humans

Image HSV: RBG for humans

This post is about what the HSV (Hue, Saturation, Value) representation of an image is.


If I told you to visualize the color (125, 75, 200), what color do you see? 🤔

🧍When describing a color, as a human of course, you might say things like: “deep red” or “pastel purple” or “bright and muted orange."

🤖 Whereas a computer (and hardware) would say something like: RED: 125, GREEN: 75, BLUE:  200.

What is intuitive for us is different than what is logical for hardware. So in the 1970’s the Hue Saturation Value system (HSL, too) was invented to represent RGB color values in terms more intuitive to humans.

  • Hue (H): the color (red, purple, green, ..). It’s a spectrum represented in degrees (red: 0°, green: 120°, blue 240°). 
  • Saturation (S): the intensity of the color. When describing a colors, it’s often the adjective.
  • Value (V): the brightness.
    • Imagine that you were mixing paint 🎨 to make a color, the “value” would be how much white or black you mixed in (to brighten or darken it).

Consider the following...

  • “Light sky blue”: light (V), sky (S), blue (H)
  • “Muted deep red”: muted (V), deep (S), red (H)]

A HSV cone is a common way to visualize it:

HSV color model single hex cone [10][14]. | Download ...
HSV Color Cone

Converting from RBG to HSV

Let's look at the RBG to HSV equations and try to understand what they're describing. They look complicated at first glance:

(equations reference)

Starting with the easiest:

VALUE: V = Cmax, is the most dominant color, i.e: the highest value (R, G, or B) of an RGB pixel. So if R = 200, and G and B are 100, then V is 200.

SATURATION: S = (Cmax - Cmin)/ Cmax, is the ratio between the highest and lowest RGB value divided by Cmax to normalize the value between 0 to 1. The exception if the pixel is black, in which case we assume 0 saturation.

HUE: H, returns an angle value between 0 to 360.

  • If the brightest of the RGB values is Red, we expect the angle to be between 0° to 120°
  • If the brightest of the RGB values is Green, we expect the angle to be between 120° to 240°
  • If the brightest of the RGB values is Blue, we expect the angle to be between 240° to 360°

Code: RBG to HSV

Here's an implementation of RGB to HSV using rust:

pub fn rgb_to_hsv(r: u8, g: u8, b: u8) -> (f32, f32, f32) {
    let r = r as f32 / 255.0;
    let g = g as f32 / 255.0;
    let b = b as f32 / 255.0;

    let max = r.max(g).max(b);
    let min = r.min(g).min(b);
    let delta = max - min;

    let h = if delta == 0.0 {
        0.0
    } else if max == r {
        60.0 * (((g - b) / delta) % 6.0)
    } else if max == g {
        60.0 * (((b - r) / delta) + 2.0)
    } else {
        60.0 * (((r - g) / delta) + 4.0)
    };

    let h = if h < 0.0 { h + 360.0 } else { h };
    let s = if max == 0.0 { 0.0 } else { delta / max };
    let v = max;

    (h, s, v)
}

Code on github.

Next time...

In my next post, I'll walk through how to adjust the "saturation" of an image, which is easy if you already have the HSV representation of the image.