The Physics of Colour


Colour is the response of the human vision system to electromagnetic radiation in the range of 400 nm to 700 nm. Strictly speaking, we can extend the range as 380 nm to 780 nm.

Colour is thus represented by a Spectral Power Distribution Function or SPDF.

Humans see blue colours if the SPDF is skewed towards the shorter wavelengths (< 480 nm). At middle wavelengths (around 550 nm), we see green colours and red colours at longer wavelengths (> 620 nm). All the colours in the rainbow (VIBGYOR) are seen as we go from 400 nm to 700 nm.

In this notebook, an SPDF is a 61-element numpy array where each value is a relative or normalised power at every 5 nm between 400 nm and 700 nm (both inclusive). The equivalent colour as seen by the human vision system is displayed in a rectangular button using tkinter module.


In [ ]:
# First, import all the necessities
import numpy as np
import tkinter as tk
import colour_calcs_3 as cc
import gauss_func as gf
from tkinter import colorchooser as cch
from matplotlib import pyplot as plt
np.set_printoptions(precision=4, suppress=True)
In [ ]:
# Functions that generate random SPDFs and 
# compute the RGB values from SPDFs
us, sunspec = cc.rgb2spdf(183, 156, 137)

def generate_random_spdf() :
    peak = np.array([400,450,500,550,600,650,700])
    pwidth = np.random.random(peak.shape[0]) * 100
    bright = np.random.random(peak.shape[0])
    bright = bright/bright.sum()

    wave, spdf = gf.gauss_spdf(peak, pwidth, bright, ulim=700, llim=400)
    return wave, spdf

def spdf2rgb(spdf) :
    xyz = cc.spdf2xyz(spdf/spdf.sum(), 0)
    if xyz[0] > 0.5 :
        xyz = cc.spdf2xyz(spdf/spdf.sum(), 1)
    rgb = np.array(cc.xyz2rgb(xyz[0], xyz[1], xyz[2])) * 255.
    rgb = rgb.astype('uint8')
    hexrgb = "#%02x%02x%02x" % (rgb[0], rgb[1], rgb[2])
    return rgb, hexrgb
In [ ]:
# Use the tkinter module to create a button for displaying the
# colours corresponding to different SPDFs
top = tk.Tk()

cb = tk.Button(height=10, width=40, command=top.destroy)
cb.pack()
for lambs in range(1,12) :
    wavels, spectrum = gf.gauss_spdf(440, lambs * 15, 1., 
                                     llim=400, ulim=700)
#    wavels, spectrum = generate_random_spdf()
    rvt = spdf2rgb(spectrum)
    tstr = str(rvt[0]) + '\tPeak at: ' + str(lambs)
    cb.configure(bg=rvt[1], text=tstr)
    cb.update()

    input('Press a key')
    
top.mainloop()