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

2012-10-06

Example of Trigonometric Functions in Matplotlib

The matplotlib library offers excellent functionality for quick and simple graphing capabilities in python.

Excellent tutorials exist about how to use matplotlib for common graphing tasks. The matplotlib documentation of scipy by Mike Müller and the  Matplotlib Tutorial by Nicolas P. Rougier offer excellent code snippets that illustrate simple graphing in matplotlib.

I especially like Rougier's "Devil is in the Details" example, which I've modelled the following example on, to show the graphs of sine, cosecant, cosine, secant, tangent, and cotangent.

Here is my spin on this, code and results below.

#!/usr/bin/python
from pylab import *

# rendering area
figure(figsize=(8,5), dpi=80)

# display area to use; can be modified to accomodate more graphs
subplot(111)

# range
x = np.linspace(0, (2 * np.pi), 256,endpoint=True)

# formulas to graph
sine = np.sin(x)
cosine = np.cos(x)
tangent = np.tan(x)
cotangent = 1/np.tan(x)
cosecant = 1/np.sin(x)
secant = 1/np.cos(x)

# line styles and labels
plot(x, sine, color="red", linewidth=2.5, linestyle="-", label="sin")
plot(x, cosine, color="blue", linewidth=2.5, linestyle="-", label="cos")
plot(x, tangent, color="orange", linewidth=2.5, linestyle="-", label="tan")
plot(x, cotangent, color="purple", linewidth=2.5, linestyle="-", label="cot")
plot(x, cosecant, color="green", linewidth=2.5, linestyle="-", label="csc")
plot(x, secant, color="yellow", linewidth=2.5, linestyle="-", label="sec")

# tick spines
ax = gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))

# x tick limits and labels
xlim(x.min()*1.1, x.max()*1.1)
xticks([(-2 * np.pi), (-3 * np.pi/2), -np.pi, -np.pi/2, 0, np.pi/2, np.pi, (3 * np.pi/2), (2 * np.pi)], [r'$-2\pi$', r'$-3/2\pi$', r'$-\pi$', r'$-\pi/2$', r'$0$', r'$+\pi/2$', r'$+\pi$', r'$3/2\pi$', r'$2\pi$'])

# y tick limits and labels
ylim(-4, 4)
yticks([-4, -3, -2, -1, +1, +2, +3, +4], [r'$-4$', r'$-3$', r'$-2$', r'$-1$', r'$+1$', r'$+2$', r'$+3$', r'$+4$'])

# legend
legend(loc='upper left')

for label in ax.get_xticklabels() + ax.get_yticklabels():
  label.set_fontsize(16)
  label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.65 ))

# display
show()




2012-09-14

How to Use Python to Upload a Document to Google Docs or Google Drive


I've been using Google Docs for several years, and recently participated in a kind of unexpected migration to Google Drive. I like the overall ease of use of the "Docs" part of Google Drive, and will hereforwards refer to this simply as "Google Docs".

I have been wanting to know for some time how to go about uploading a document to Google Docs via some kind of API. The Google Documents List API provides this functionality, and there are some useful examples on how to employ the Python version to upload files, as shown by Google here, and as seen in various postings on sites such as Stack Overflow, such as an example seen here.

Here is my version of how to upload a file to Google Docs, based on examples I mentioned above, available at http://github.com/mikequentel/google-drive-python In order to use this script, Google Documents List API version 3.0 is required, but this can be downloaded as the Google APIs Client Library for Python.

#!/usr/bin/python

import sys
import argparse
import os
import gdata.data
import gdata.docs.client
import gdata.docs.data
import gdata.docs.service
import gdata.sample_util

class SampleConfig(object):
  APP_NAME = 'GDataDocumentsListAPISample-v1.0'
  DEBUG = False

def create_client():
  client = gdata.docs.client.DocsClient(source=SampleConfig.APP_NAME)
  try:
    gdata.sample_util.authorize_client(
       client,
       1,
       service=client.auth_service,
       source=client.source,
       scopes=client.auth_scopes
    )
  except gdata.client.BadAuthentication:
    exit('Invalid user credentials given.')
  except gdata.client.Error:
    exit('Login Error')
  return client

def upload_file(path, title, type, do_convert):
  client = create_client()
  doc = gdata.docs.data.Resource(type=type, title=title)
  media = gdata.data.MediaSource()
  media.SetFileHandle(path, type)
  create_uri = gdata.docs.client.RESOURCE_UPLOAD_URI + '?convert=' + do_convert
  doc = client.CreateResource(doc, create_uri=create_uri, media=media)
  print 'Uploaded:', doc.title.text, doc.resource_id.text

def main():
  parser = argparse.ArgumentParser(description='Upload file to Google Drive (previously known as \'Google Docs\').')
  parser.add_argument('file', help='file to upload.')
  parser.add_argument('title', help='title for the uploaded document.')
  parser.add_argument('type', nargs='?', default='text/plain', help='type of file to upload, default is text/plain.')
  parser.add_argument('do_convert', nargs='?', default='true', help='true or false: convert the document to Google Docs format? Default is true')
  args = parser.parse_args()
  path = args.file
  title = args.title
  type = args.type
  do_convert = args.do_convert
  upload_file(path, title, type, do_convert)
  os._exit(0)

# Specifies name of main function.
if __name__ == "__main__":
  sys.exit(main())

2012-06-26

Using Rule of Sarrus, Cramer's Rule, and Python to Solve a System of Three Linear Equations

Lately I've been studying how to solve a system of linear equations; in particular, three linear equations. Not satisfied with the "Elimination of Variables" method for solving this sort of problem, I researched other ways of solving for x, y, and z values. I found that the "Rule of Sarrus" and "Cramer's Rule" are ideal for my purposes, and put these concepts into a Python script that would illustrate the rules. One can download the source code for this at: https://github.com/mikequentel/sarrus_cramer

Quick background on Rule of Sarrus and Cramer's Rule...
  1. Rule of Sarrus computes the determinant of a 3x3 matrix.
  2. Cramer's Rule solves a system of linear equations (where number of equations is equal to number of unknowns) using determinants (which can be obtained from the above Rule of Sarrus). Purple Math has an excellent explanation and use case for Cramer's Rule at: http://www.purplemath.com/modules/cramers.htm
  3. For a system of three linear equations, one can use the above rules to provide a solution, summarised as the following:
  4. x = det(x)/det(coefficients)
    y = det(y)/det(coefficients)
    z = det(z)/det(coefficients)
    
    
I must mention that the code below is very simple, and lacks error-trapping (for example, nothing traps for division by zero). Reason for this rough implementation is to keep the code readable as an example of using the rules of Sarrus and Cramer, rather than an actual implementation that would be used in a real system.
# System of three linear equations
# ax + by + cz = j
# dx + ey + fz = k
# gx + hy + iz = l

# System of three linear equations in matrix notation
#  -         -   - -       - -
# | a   b   c | | x |     | j |
# |           | |   |     |   |
# | d   e   f | | y |  =  | k |
# |           | |   |     |   |
# | g   h   i | | z |     | l |
#  -         -   - -       - -

# Matrix of Coefficients
# a b c
# d e f
# g h i

# Matrix of Variables
# x
# y
# z

# Matrix of Resulting Values
# j
# k
# l

# Rule of Sarrus
# a b c|a b
# d e f|d e
# g h i|g h

# Rule of Sarrus Index Values
# 0 1 2|0 1
# 3 4 5|3 4
# 6 7 8|6 7

# Determinant
# det(M) = aei + bfg + cdh - gec - hfa - idb

# Cramer's Rule
# | j b c |   | a j c |   | a b j |
# | k e f |   | d k f |   | d e k |
# | l h i |   | g l i |   | g h l |
# ---------,  ---------,  ---------
# | a b c |   | a b c |   | a b c |
# | d e f |   | d e f |   | d e f | 
# | g h i |   | g h i |   | g h i |
import sys

def main():
    
    inputs_dict = {'a':int(raw_input("a:")), 'b':int(raw_input("b:")), 'c':int(raw_input("c:")), 'j':int(raw_input("j:")), 
                   'd':int(raw_input("d:")), 'e':int(raw_input("e:")), 'f':int(raw_input("f:")), 'k':int(raw_input("k:")), 
                   'g':int(raw_input("g:")), 'h':int(raw_input("h:")), 'i':int(raw_input("i:")), 'l':int(raw_input("l:"))}

    coeffs_matrix = {'a':inputs_dict['a'], 'b':inputs_dict['b'], 'c':inputs_dict['c'], 
                     'd':inputs_dict['d'], 'e':inputs_dict['e'], 'f':inputs_dict['f'], 
                     'g':inputs_dict['g'], 'h':inputs_dict['h'], 'i':inputs_dict['i']}
    x_numerator_matrix = {'j':inputs_dict['j'], 'b':inputs_dict['b'], 'c':inputs_dict['c'], 
                          'k':inputs_dict['k'], 'e':inputs_dict['e'], 'f':inputs_dict['f'], 
                          'l':inputs_dict['l'], 'h':inputs_dict['h'], 'i':inputs_dict['i']}
    y_numerator_matrix = {'a':inputs_dict['a'], 'j':inputs_dict['j'], 'c':inputs_dict['c'], 
                          'd':inputs_dict['d'], 'k':inputs_dict['k'], 'f':inputs_dict['f'], 
                          'g':inputs_dict['g'], 'l':inputs_dict['l'], 'i':inputs_dict['i']}
    z_numerator_matrix = {'a':inputs_dict['a'], 'b':inputs_dict['b'], 'j':inputs_dict['j'], 
                          'd':inputs_dict['d'], 'e':inputs_dict['e'], 'k':inputs_dict['k'], 
                          'g':inputs_dict['g'], 'h':inputs_dict['h'], 'l':inputs_dict['l']}
    
    # Rule of Sarrus for det_coeffs_matrix
    # a b c|a b
    # d e f|d e
    # g h i|g h
    #
    det_coeffs_matrix = (coeffs_matrix['a'] * coeffs_matrix['e'] * coeffs_matrix['i'] +
                         coeffs_matrix['b'] * coeffs_matrix['f'] * coeffs_matrix['g'] +
                         coeffs_matrix['c'] * coeffs_matrix['d'] * coeffs_matrix['h'] -
                         coeffs_matrix['g'] * coeffs_matrix['e'] * coeffs_matrix['c'] -
                         coeffs_matrix['h'] * coeffs_matrix['f'] * coeffs_matrix['a'] -
                         coeffs_matrix['i'] * coeffs_matrix['d'] * coeffs_matrix['b'])
    
    # Rule of Sarrus for det_x_numerator_matrix
    # j b c|j b
    # k e f|k e
    # l h i|l h
    #
    det_x_numerator_matrix = (x_numerator_matrix['j'] * x_numerator_matrix['e'] * x_numerator_matrix['i'] +
                              x_numerator_matrix['b'] * x_numerator_matrix['f'] * x_numerator_matrix['l'] + 
                              x_numerator_matrix['c'] * x_numerator_matrix['k'] * x_numerator_matrix['h'] -
                              x_numerator_matrix['l'] * x_numerator_matrix['e'] * x_numerator_matrix['c'] -
                              x_numerator_matrix['h'] * x_numerator_matrix['f'] * x_numerator_matrix['j'] -
                              x_numerator_matrix['i'] * x_numerator_matrix['k'] * x_numerator_matrix['b'] )
    
    # Rule of Sarrus for det_y_numerator_matrix
    # a j c|a j
    # d k f|d k
    # g l i|g l
    #
    det_y_numerator_matrix = (y_numerator_matrix['a'] * y_numerator_matrix['k'] * y_numerator_matrix['i'] +
                              y_numerator_matrix['j'] * y_numerator_matrix['f'] * y_numerator_matrix['g'] +
                              y_numerator_matrix['c'] * y_numerator_matrix['d'] * y_numerator_matrix['l'] -
                              y_numerator_matrix['g'] * y_numerator_matrix['k'] * y_numerator_matrix['c'] -
                              y_numerator_matrix['l'] * y_numerator_matrix['f'] * y_numerator_matrix['a'] -
                              y_numerator_matrix['i'] * y_numerator_matrix['d'] * y_numerator_matrix['j'])
    
    # Rule of Sarrus for det_z_numerator_matrix
    # a b j|a b
    # d e k|d e
    # g h l|g h
    #
    det_z_numerator_matrix = (z_numerator_matrix['a'] * z_numerator_matrix['e'] * z_numerator_matrix['l'] +
                              z_numerator_matrix['b'] * z_numerator_matrix['k'] * z_numerator_matrix['g'] +
                              z_numerator_matrix['j'] * z_numerator_matrix['d'] * z_numerator_matrix['h'] -
                              z_numerator_matrix['g'] * z_numerator_matrix['e'] * z_numerator_matrix['j'] -
                              z_numerator_matrix['h'] * z_numerator_matrix['k'] * z_numerator_matrix['a'] -
                              z_numerator_matrix['l'] * z_numerator_matrix['d'] * z_numerator_matrix['b'])
    
    x = det_x_numerator_matrix/det_coeffs_matrix
    y = det_y_numerator_matrix/det_coeffs_matrix
    z = det_z_numerator_matrix/det_coeffs_matrix

    print
    print "results: "
    print "x = " + str(x)
    print "y = " + str(y)
    print "z = " + str(z)
    
    
# Specifies name of main function.
if __name__ == "__main__":
    sys.exit(main())