Thursday, February 25, 2010

Transforming RGB data to wavelength information

Here, I'm presenting a reverse transformation to wavelength to RGB. Right now I have no means of testing its accuracy on some real data. As a first check, I've run it on the mercury lamp image shown in my older post. There are small deviations caused probably by low intensity of light in some places. If used on some real stuff, I would definitely attempt to first fit a line (or some other meaningful function) through the estimated wavelengths. Then of course plot the spectra against fitted values. The domain of this function is limited to 380 - 645 nm. So, here's the algorithm in Python:

def RGB2lambda(R, G, B):
"""Returns 0 if indeciferable"""
# selects range by maximum component
# if max is blue - range is 380 - 489
# if max is green - range is 490 - 579
# if max is red - range is 580 - 645

# which colour has highest intensity?
high = float(R)
highind = 1
if G > high:
high = float(G)
highind = 2
if B > high:
high = float(B)
highind = 3

# normalize highest to 1.0
RGBnorm = [R / high, G / high, B / high]

# start decoding
RGBlambda = 0
if highind == 1: # red is highest
if B >= G: # there is more blue than green
return 0 # max red and more blue than green shouldn't happen
# wavelength linearly changes from 645 to 580 as green increases
RGBlambda = 645 - RGBnorm[1] * (645. - 580.)

elif highind == 2: # green is max, range is 510 - 579
if R > B: # range is 510 - 579
RGBlambda = 510 + RGBnorm[0] * (580 - 510)
else: # blue is higher than red, range is 490 - 510
RGBlambda = 510 - RGBnorm[2] * (510 - 490)

elif highind == 3: # blue is max, range is 380 - 490
if G > R: # range is 440 - 490
RGBlambda = RGBnorm[1] * (490 - 440) + 440
else: # there is more red than green, range is 380 - 440
RGBlambda = 440 - RGBnorm[0] * (440 - 380)

return RGBlambda

And here is an accuracy check made on my older image of mercury lamp spectrum:
Accuracy check (synthetic test): x-axis is expected value, y-axis is algorithm output. Circles are the data, the ideal is presented by red line (y = x).

Please note that this was NOT produced by using a real photo of a spectrum. As someone who has some spectroscopic background I strongly discourage you from using this algorithm to get wavelength information. This was derived from a simplified version of simple wavelength-to-RGB algorithm. Nevertheless, I intend to make some photos of monochromatic light and test it ;-)


Anonymous said...

Hi, I was wondering if you have continued work on this at all.
I was looking to use a program like this to compare acid/base indicators to each other in a high school science classroom.
Take a pic, average out the color values of the indicator, plug the values into this program and then compare. Thanks for doing all the work like this, wondering if you've developed it further.

R.L. said...

Hi, I did take the pics of monochromatic light with a couple of cameras. I didn't take it any further. I remember it was difficult to stay within the dynamic range of the cameras (you cannot be saturated on any channel, cheap cameras are not easy in this regard).
To your idea: I'm not sure what exactly you want to achieve. I think you should be able to detect the color change accompanying the pH change if you tune your system well.
I looked (very) briefly at phenolphtalein behavior (I forgot a lot since high school..) and I guess you should be able to detect the color change pretty well just by watching e.g. the ratio between R(ed) and B(lue) channel from your pic.
You mention comparing several indicators -- I don't understand exactly what you want to achieve but I think this might be very difficult without proper absorption measurement.
The code from this post should work somewhat if you really have monochromatic signal. I'm afraid this won't be the case with colors of many chemical compounds.
We don't have any common pH indicator dye in the lab so I cannot test anything right now.