Hue Luminance and Saturation (HLS) Model and Manipulating Colours

Pick and manipulate colours more easily

Hue Luminance and Saturation Demonstration

Normally in computers colours are described in terms of their Red, Green and Blue components. Whilst you can specify all displayable colours this way, it leaves something to be desired when it comes to picking a colour. For example, most people find it very difficult to determine what RGB values you would use to create a Pink or Brown colour (try it!). The HLS model provides a more intuitive method and enables you to create lighter/darker or more intense/less intense versions of colours very easily.

The Hue, Luminance and Saturation Model

There are numerous colour models which attempt to work around the limitations of RGB and provide a more intuitive way of getting to the colour you want. One of the easiest, and most frequently used, is the Hue, Luminance and Saturation (HLS) Model.

This HLS model describes colours in the following terms:

  • Hue
    The horizontal axis of the box in the demonstration project picture, which varies from magenta - red - yellow - green - cyan - blue - magenta.
  • Saturation
    The vertical axis of the box, and describes how intense (saturated) or "grey" (unsaturated) the colour is.
  • Luminance
    The second picture box in the demonstration project picture, which and varies from black (0 luminace) through to the brightest version of the colour itself.

With this model it is easier to pick a colour and it allows you to do things in code which you can't really achieve with RGB. Examples are determining what a lighter or darker tone of a given RGB colour is (by modifying the Luminance), creating a more washed out colour (by modifying the Saturation) or colourising a whole image (by modifying the Hue).

Coding the HLS Model

Normally Hue is expressed as an angle between 0-360 to describe the colour and a value between 0 and 1 (a percentage) to describe Hue and Saturation. I have missed out the conversion to an angle in this implementation so the Hue works as follows:

Hue   Colour 
 -1   Magenta 
  0   Red 
  1   Yellow 
  2   Green 
  3   Aqua 
  4   Blue 
  5   Magenta 

Here is the code to do a conversion between HLS and RGB and vice-versa:

Public Sub RGBToHLS( _ 
    ByVal r As Long, ByVal g As Long, ByVal b As Long, _ 
    h As Single, s As Single, l As Single _ 
    ) 
Dim Max As Single 
Dim Min As Single 
Dim delta As Single 
Dim rR As Single, rG As Single, rB As Single 

    rR = r / 255: rG = g / 255: rB = b / 255 

    '{Given: rgb each in [0,1]. 
    ' Desired: h in [0,360] and s in [0,1], except if s=0, then h=UNDEFINED.} 
    Max = Maximum(rR, rG, rB) 
    Min = Minimum(rR, rG, rB) 
    l = (Max + Min) / 2'{This is the lightness} 
        
    '{Next calculate saturation} 
    If Max = Min Then 
        'begin {Acrhomatic case} 
        s = 0 
        h = 0 
        'end {Acrhomatic case} 
    Else 
        'begin {Chromatic case} 
        '{First calculate the saturation.} 
        If l <= 0.5 Then 
            s = (Max - Min) / (Max + Min) 
        Else 
            s = (Max - Min) / (2 - Max - Min) 
        End If 
        '{Next calculate the hue.} 
        delta = Max - Min 
        If rR = Max Then 
            '{Resulting color is between yellow and magenta} 
            h = (rG - rB) / delta
        ElseIf rG = Max Then 
            '{Resulting color is between cyan and yellow}  
            h = 2 + (rB - rR) / delta 
        ElseIf rB = Max Then 
            '{Resulting color is between magenta and cyan} 
            h = 4 + (rR - rG) / delta 
        End If 
        'end {Chromatic Case} 
    End If 
End Sub 

Public Sub HLSToRGB( _ 
    ByVal h As Single, ByVal s As Single, ByVal l As Single, _ 
    r As Long, g As Long, b As Long _ 
    ) 
Dim rR As Single, rG As Single, rB As Single 
Dim Min As Single, Max As Single 

    If s = 0 Then 
        ' Achromatic case: 
        rR = l: rG = l: rB = l 
    Else 
        ' Chromatic case: 
        ' delta = Max-Min 
        If l <= 0.5 Then 
            's = (Max - Min) / (Max + Min) 
            ' Get Min value: 
            Min = l * (1 - s) 
        Else 
            's = (Max - Min) / (2 - Max - Min) 
            ' Get Min value: 
            Min = l - s * (1 - l) 
        End If 
        ' Get the Max value: 
        Max = 2 * l - Min 
    
        ' Now depending on sector we can evaluate the h,l,s: 
        If (h < 1) Then 
            rR = Max 
            If (h < 0) Then 
                rG = Min 
                rB = rG - h * (Max - Min) 
            Else 
                rB = Min 
                rG = h * (Max - Min) + rB 
            End If 
        ElseIf (h < 3) Then 
            rG = Max 
            If (h < 2) Then 
                rB = Min 
                rR = rB - (h - 2) * (Max - Min) 
            Else 
                rR = Min 
                rB = (h - 2) * (Max - Min) + rR 
            End If 
        Else 
            rB = Max 
            If (h < 4) Then 
                rR = Min 
                rG = rR - (h - 4) * (Max - Min) 
            Else 
                rG = Min 
                rR = (h - 4) * (Max - Min) + rG 
            End If         
        End If             
    End If 
    r = rR * 255: g = rG * 255: b = rB * 255 
End Sub 

Private Function Maximum(rR As Single, rG As Single, rB As Single) As Single 
    If (rR > rG) Then 
        If (rR > rB) Then 
            Maximum = rR 
        Else 
            Maximum = rB 
        End If 
    Else 
        If (rB > rG) Then 
            Maximum = rB 
        Else 
            Maximum = rG 
        End If 
    End If 
End Function 

Private Function Minimum(rR As Single, rG As Single, rB As Single) As Single 
    If (rR < rG) Then 
        If (rR < rB) Then 
            Minimum = rR 
        Else 
            Minimum = rB 
        End If 
    Else 
        If (rB < rG) Then 
            Minimum = rB 
        Else 
            Minimum = rG 
        End If 
    End If 
End Function 

Limitations of the HLS Model

Whilst the HLS Model is simple to implement, and gives a relatively easy colour picking interface, it is not a physically accurate model. That is to say, linear changes in luminance, hue or saturation do not correspond to a linear change in colour to the eye. More accurate models which overcome these limitations exist, such as the CIE scheme, but I've yet to understand these enough to work them out! Anyway, HLS works well enough unless you are interested in exact colour matching and processing between different display devices.