2012-12-05

RGB and HSV Colour Selector Demo in Python

The use of red-green-blue (RGB) tuples along with hue-saturation-value (HSV) tuples is quite common in many software, such as in the colour selection feature of GIMP. Alvy Ray Smith describes the  conversion between the two colour spaces in an article Color Gamut Transform Pairs” in the August 1978 issue of SIGGRAPH 78 Conference Proceedings.

Based on the algorithms in Smith's article, I've implemented a Python colour selector demo (downloadable at http://github.com/mikequentel/rgb_hsv or http://downloads.mikequentel.com/rgbhsv.py.tar.gz) to show how a colour can be set using either RGB or HSV settings.  The code listing of the demo shows even the same variable naming conventions as the pseudocode in Smith's article, to make it easier to compare to the algorithm.

Here is a screenshot of the demo in action:


The demo uses a Tkinter GUI and the standard, out-of-the box library functionality that comes with Python 2.7. The colour swatch (rectangle) is simply the background of a Canvas object. This is meant to be a very simple, easy-to-understand demo of how to convert between RGB and HSV. The code is not meant to be an optimised example of how to generate GUI widgets; no special design patterns that generalise the creation of widgets are used.

As the demo has about 300 lines of code, I will only show here snippets of the functions that convert between RGB and HSV.

def rgb2hsv(R, G, B):
  print "Start of rgb2hsv()"
 
  if R == G == B == 0.0:
    return {'h':0, 's':0, 'v':0}
 
  # DETERMINES MAXIMUM RGB VALUE "V" WHICH IS A MEASURE OF THE DEPARTURE
  # FROM BLACK.
  V = max(R, G, B)
 
  # DETERMINES MININUM RGB VALUE "X".
  X = min(R, G, B)
 
  # DETERMINES SATURATION "S".
  S = (V - X)/V
 
  # DETERMINES ADJUSTED RED, GREEN, BLUE VALUES "r", "g", "b".
  r = (V - R)/(V - X)
  g = (V - G)/(V - X)
  b = (V - B)/(V - X)
 
  # DETERMINES HUE "H"
  H = 0
  if R == V:
    H = G == X and 5 + b or 1 - g
  if G == V:
    H = B == X and 1 + r or 3 -b
  else:
    H = R == X and 3 + g or 5 - r
  H /= 6.0
 
  hue = toDegrees(H)
  saturation = toRoundedPercentage(S)
  value = toRoundedPercentage(V)

  return {'h':hue, 's':saturation, 'v':value}

def hsv2rgb(H, S, V):
  print "Start of hsv2rgb()"
 
  if H == S == V == 0.0:
    return {'r':0, 'g':0, 'b':0}
 
  H *= 6
  I = math.floor(H)
  F = H - I
  M = V * (1 - S)
  N = V * (1 - S * F)
  K = V * (1 - S * (1 - F))
 
  R = G = B = 0.0
  if I == 0:
    R = V
    G = K
    B = M
  elif I == 1:
    R = N
    G = V
    B = M
  elif I == 2:
    R = M
    G = V
    B = K
  elif I == 3:
    R = M
    G = N
    B = V
  elif I == 4:
    R = K
    G = M
    B = V
  else:
    R = V
    G = M
    B = N

  red = to8bit(R)
  green = to8bit(G)
  blue = to8bit(B)
 
  return {'r':red, 'g':green, 'b':blue}


The functions closely match the algorithm pseudocodes in Smith's article, and even have the same variable names.

This demo was a good learning experience in understanding the algorithms that transform between the colour models of RGB and HSV.

UPDATE February 2017: there also now exists an Amazon Web Services (AWS) Lambda version of the conversion functions used in the demo--source code available at: https://github.com/mikequentel/rgb_hsv/blob/master/lambda_rgbhsv.py

UPDATE November 2017: there also now exists a Docker-based version of the Colour Selector Demo--see the Dockerfile and the script docker_buildrun.sh at https://github.com/mikequentel/rgb_hsv

No comments: