Hacking light spectral distribution charts exported from a Sekonic C700 handheld spectrometer

This notebook shows how to :

  • Extract the spectral distribution data from charts exported from a Sekonic C700 handheld spectrometer
  • Save that data to various file formats
  • Draw ciustom charts of spectral distribution
  • Make custom computation on the spectral data

In [160]:
# First a little bit of boiler plate

# Libraries imports
%matplotlib inline
import collections
import colour
from colour.plotting import *
import json
import math
import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
from skimage import data, io, filters

colour.filter_warnings(True, False)
colour_plotting_defaults()

# File extention to use when saving to text file with json format
jsonExtension=".json"
# File extention to use when saving to binary file with numpy format
numpyExtension=".npy"

# min value of C700 charts Y axis
ymin = 0
# max y value depends on the chart. 
# It is configured below at the same time as the chartt file name

#Wavelength range of C700 charts X axis
xmin = 380
xmax = 780

## Path to sample spectral distribution charts from my C700 ##
#
# ymax is the range of the chart, in W.m-2.nm-1, of the chart Y axis 
# chartname is the file name of the chart image, without the full folder 
# path or the extention. Those are defined above
#
# It is recommeded to export the charts of the C700 using PNG file format
# This is to avoid issues with compression artifacts of JPEG format.
#
# You can choose a different sample chart or select your own chart by changing the "filepath" and
# "ymax, chartname" variables below.  that is uncommented below and the  above 
# Exports to other formats will be saved in the same "filepath" folder as the source charts.

## ToDo : Use OCR to automatically get ymax from the chart

# Path to the folder containing the spectral distribution charts exported from a Sekonic C700
filepath="/Users/b12/Documents/SEKONIC/C-700Series/SpectralDist/"

# File extention of the spectral distribution charts exported from a Sekonic C700
chartExtension=".png"

# 10W Range charts
#ymax, chartname = 10, "Red-Calib-125mV_01_Under_SpectralDistribution"

# 5W Range sample charts
#ymax, chartname = 5, "Sun-Calib-427mV_01_5016K_SpectralDistribution"
#ymax, chartname = 5, "GG-UFO_01_Under_SpectralDistribution"
#ymax, chartname = 5, "GG-UFO_02_Under_SpectralDistribution"
#ymax, chartname = 5, "GG-UFO_04_Under_SpectralDistribution"

# 1W Range sample charts
#ymax, chartname = 1, "Sun-Calib-344mV_01_5119K_SpectralDistribution"
#ymax, chartname = 1, "FS-230W-45.7mV_01_Under_SpectralDistribution"
#ymax, chartname = 1, "LED-Blue_02_Over_SpectralDistribution"
#ymax, chartname = 1, "LED-Blue_01_Over_SpectralDistribution"
#ymax, chartname = 1, "LED-Yellow_01_2034K_SpectralDistribution"
#ymax, chartname = 1, "GG-UFO_03_Over_SpectralDistribution"
#ymax, chartname = 1, "LED-Cyan_01_Over_SpectralDistribution"
#ymax, chartname = 1, "LED-Green_01_Over_SpectralDistribution"
#ymax, chartname = 1, "LED-Deep-Red_01_Under_SpectralDistribution"
#ymax, chartname = 1, "CFL_02_2584K_SpectralDistribution"
#ymax, chartname = 1, "LED-FS-Chanzon_07_Under_SpectralDistribution"
#ymax, chartname = 1, "LED-FS-GG_01_Under_SpectralDistribution"
#ymax, chartname = 1, "LED-FS-MingBen_01_Under_SpectralDistribution"

# 0.5 W Range sample charts
#ymax, chartname = 0.5, "Red-Calib-4.7mV_01_Under_SpectralDistribution"
# 0.2 W Range sample charts
#ymax, chartname = 0.2, "LED-Blue-1.5mV_01_Over_SpectralDistribution"
#ymax, chartname = 0.2, "Sun-Calib-75.7mV_01_5134K_SpectralDistribution"

#GG Samples
#ymax, chartname = 5, "GG-Flo_01_FS"
#ymax, chartname = 1, "GG-Flo_02_FS"
#ymax, chartname = 1, "GG-Flo_03_FS"
#ymax, chartname = 0.2, "GG-Flo_04_CFL"
#ymax, chartname = 1, "GG-Flo_06_HPS"
#ymax, chartname = 5, "GG-Flo_08_HPS"
#ymax, chartname = 5, "GG-Flo_05_HPS"

# Red LED spatial distribution test
#ymax, chartname = 0.5, "LED-Red-3A-1m_01_Under_SpectralDistribution"
#ymax, chartname = 50E-3, "LED-Red-3A-1m_02_Under_SpectralDistribution"
#ymax, chartname = 0.1, "LED-Red-3A-1m_03_Under_SpectralDistribution"
#ymax, chartname = 0.1, "LED-Red-3A-1m_04_Under_SpectralDistribution"
#ymax, chartname = 0.2, "LED-Red-3A-1m_05_Under_SpectralDistribution"
#ymax, chartname = 0.2, "LED-Red-3A-1m_06_Under_SpectralDistribution"
#ymax, chartname = 0.5, "LED-Red-3A-1m_07_Under_SpectralDistribution"
#ymax, chartname = 0.5, "LED-Red-3A-1m_08_Under_SpectralDistribution"
#ymax, chartname = 0.2, "LED-Red-3A-1m_09_Under_SpectralDistribution"
#ymax, chartname = 0.2, "LED-Red-3A-1m_10_Under_SpectralDistribution"


ymax, chartname = 0.5, "GG-Flo_01_Douche"
#ymax, chartname = 1, "GG-Flo_02_Carre"
#ymax, chartname = 5, "GG-Flo_03_UFO-1"
#ymax, chartname = 5, "GG-Flo_04_UFO-2"



filename = ''.join([filepath,chartname,chartExtension])

print("")
print("Full chart file name :")
print("")
print(filename)
print("")

# Load image using skimage.io
chart_image = io.imread(filename)

# Show the type of data structure used to store the image in memory
print("")
print ("chart_image type is : ")
print (type(chart_image))
# Show the dimentions of the ndarray.
print("")
print ("Image/Array dimention : ")
print (chart_image.shape)
# check image dimention. It should be (1366, 1940, 4)
assert(chart_image.shape == (1366, 1940, 4))

# Show the image
#print("")
#print("Chart Image : ")
#plt_im = plt.imshow(chart_image)

# Display the y axis range part of the chart
#plt.imshow(chart_image[0:100,100:200,:])

# Display the x axis high range part of the chart. Should be 780
# plt.imshow(chart_image[1100:1300,1750:1950,:])

# Display the x axis low range part of the chart. Should be 380
# plt.imshow(chart_image[1100:1300,100:300,:])

# Crop the image along xmin/xmax/ymin/ymax to keep only the curve area 
clean_chart_image = chart_image[61:1204,181:1880,:]

print()
print("Size of the truncated image :")
print(clean_chart_image.shape)

height,width,depth = clean_chart_image.shape

# This should show the cropped chart
#plt_im = plt.imshow(clean_chart_image)

## Count curve heigh based on ration of count of pixels of non neutral 
# color compared to pixels of neutral color
# The area above the curve is either white or black. The area under 
# the curve is quite saturated rainbow color

# First build a mask by comparing the red and green channels
mask1 = clean_chart_image[:,:,0] != clean_chart_image[:,:,1]
# Then build another mask by comparing the green and blue channels
mask2 = clean_chart_image[:,:,1] != clean_chart_image[:,:,2]
# Combine both masks (dirty way to compute a Boolean OR of the 2 masks)
mask = mask1 + mask2
# Count the ratio of non neutral pixels
spectrumRaw = (ymax-ymin)*(mask.mean(axis=0))+ymin

## Aggregate the data along the x axis to keep only one point per nm and store 
# it into a dictionary with the wavelengths as the keys
spd_data = {}

# Aggregation loop
for i in range(xmin,xmax+1,1):
    x1=math.floor((i-xmin)*width/(xmax+1-xmin))   
    x2=math.ceil((i+1-xmin)*width/(xmax+1-xmin))
    #spectrumP[i]=spectrumRaw[i]*i*1e-9/(299792458*6.626E-34*6.022E23)
    #Store in dictionaries
    spd_data[i]=np.mean(spectrumRaw[x1:x2])
    #spd_dataP[i]=spectrumP[i]
    


# Save the spectral distribution data as a numpy file
filename = ''.join([filepath,chartname,numpyExtension])
np.save(filename, spd_data) 
print ("")
print ("Spectral distribution data saved to ",filename)

# Save the spectral distribution data as a json file
filename = ''.join([filepath,chartname,jsonExtension])
json.dump(spd_data, open(filename,'w'))
print ("")
print ("Spectral distribution data saved to ",filename)
print ("")

## ToDo : Save to csv format

# Make a custom plot of the spectral power distribution.
spdSample = colour.SpectralPowerDistribution('Power Density Spectral Distribution', spd_data)
single_spd_plot(spdSample, title='Power Density Spectral Distribution', y_label='Power Density (W.m-2.nm-1)')

# Make a custom plot of the spectral power distribution and save the chart to a file.
filename = ''.join([filepath,''.join([chartname,"_","Watt"]),chartExtension])

## ToDo: Save chart to image correctly (fname is a hack)
single_spd_plot(spdSample, title='Power Density Spectral Distribution', y_label='Power Density (W.m-2.nm-1)', fname=filename)

print ("chart saved to :")
print (filename)


Full chart file name :

/Users/b12/Documents/SEKONIC/C-700Series/SpectralDist/GG-Flo_01_Douche.png


chart_image type is : 
<class 'numpy.ndarray'>

Image/Array dimention : 
(1366, 1940, 4)

Size of the truncated image :
(1143, 1699, 4)

Spectral distribution data saved to  /Users/b12/Documents/SEKONIC/C-700Series/SpectralDist/GG-Flo_01_Douche.npy

Spectral distribution data saved to  /Users/b12/Documents/SEKONIC/C-700Series/SpectralDist/GG-Flo_01_Douche.json

chart saved to :
/Users/b12/Documents/SEKONIC/C-700Series/SpectralDist/GG-Flo_01_Douche_Watt.png

Now lets do some custom computations and charts with the data we have extracted and saved to files


In [161]:
# Load the data from some files that have been generated by the previous cell
filename = ''.join([filepath,'LED-Cyan_01_Over_SpectralDistribution',numpyExtension])
spd_dict_Cyan = np.load(filename).item()
spdW_ref_Cyan = colour.SpectralPowerDistribution('LED Cyan', spd_dict_Cyan)

filename = ''.join([filepath,'LED-Green_01_Over_SpectralDistribution',numpyExtension])
spd_dict_Green = np.load(filename).item()
spdW_ref_Green = colour.SpectralPowerDistribution('LED Green', spd_dict_Green)

filename = ''.join([filepath,'LED-Blue_01_Over_SpectralDistribution',numpyExtension])
spd_dict_Blue = np.load(filename).item()
spdW_ref_Blue = colour.SpectralPowerDistribution('LED Blue', spd_dict_Blue)

#filename = ''.join([filepath,'FS-230W-45.7mV_01_Under_SpectralDistribution',numpyExtension])
filename = ''.join([filepath,'LED-FS-Chanzon_07_Under_SpectralDistribution',numpyExtension])
spd_dict_FSC = np.load(filename).item()
spdW_ref_FSC = colour.SpectralPowerDistribution('LED Full Spectrum Chanzon', spd_dict_FSC)

filename = ''.join([filepath,'LED-Deep-Red_01_Under_SpectralDistribution',numpyExtension])
spd_dict_Red = np.load(filename).item()
spdW_ref_Red = colour.SpectralPowerDistribution('LED Red', spd_dict_Red)

filename = ''.join([filepath,'LED-Yellow_01_2034K_SpectralDistribution',numpyExtension])
spd_dict_Yellow = np.load(filename).item()
spdW_ref_Yellow = colour.SpectralPowerDistribution('LED Yellow', spd_dict_Yellow)

filename = ''.join([filepath,'Sun-Calib-427mV_01_5016K_SpectralDistribution',numpyExtension])
#filename = ''.join([filepath,'Sun-Calib-344mV_01_5119K_SpectralDistribution',numpyExtension])
spd_dict_Sun = np.load(filename).item()
spdW_ref_Sun = colour.SpectralPowerDistribution('Soleil 100 000 lux', spd_dict_Sun)


#filename = ''.join([filepath,'SpectralDistribution_Various_LEDs.png'])
#print (filename)

#multi_spd_plot([spdW_ref_Red, spdW_ref_Green, spdW_ref_Blue, spdW_ref_Cyan, spdW_ref_FSC, 
#                spdW_ref_Yellow, spdW_ref_Sun], bounding_box=[380,780, 0, 1.6], 
#                use_spds_colours=False, title='Various LEDs', y_label='Power Density (W.m-2.nm-1)',
#                fname=filename)

multi_spd_plot([spdW_ref_Red, spdW_ref_Green, spdW_ref_Blue, spdW_ref_Cyan, spdW_ref_FSC, 
                spdW_ref_Yellow, spdW_ref_Sun], bounding_box=[380,780, 0, 1.6], 
                use_spds_colours=False, title='Various LEDs', y_label='Power Density (W.m-2.nm-1)')



In [162]:
filename = ''.join([filepath,'LED-FS-MingBen_01_Under_SpectralDistribution',numpyExtension])
spd_dict_Ming = np.load(filename).item()
spdW_ref_Ming = colour.SpectralPowerDistribution('LED Full Spectrum Ming Ben', spd_dict_Ming)

#filename = ''.join([filepath,'SpectralDistribution_MingBen-vs-Chanzon.png'])
#print (filename)

#multi_spd_plot([spdW_ref_FSC, spdW_ref_Ming], 
#               bounding_box=[380,780, 0, 1.2], title='Ming Ben vs Chanzon', 
#               y_label='Power Density (W.m-2.nm-1)', fname=filename)

multi_spd_plot([spdW_ref_FSC, spdW_ref_Ming], 
               bounding_box=[380,780, 0, 1.2], title='Ming Ben vs Chanzon', y_label='Power Density (W.m-2.nm-1)')



In [163]:
filename = ''.join([filepath,'GG-UFO_01_Under_SpectralDistribution',numpyExtension])
spd_dict_UFO1 = np.load(filename).item()
spdW_ref_UFO1 = colour.SpectralPowerDistribution('Sous UFO 1', spd_dict_UFO1)

filename = ''.join([filepath,'GG-UFO_02_Under_SpectralDistribution',numpyExtension])
spd_dict_UFO2 = np.load(filename).item()
spdW_ref_UFO2 = colour.SpectralPowerDistribution('Sous UFO 2', spd_dict_UFO2)

filename = ''.join([filepath,'GG-UFO_03_Over_SpectralDistribution',numpyExtension])
spd_dict_UFO3 = np.load(filename).item()
spdW_ref_UFO3 = colour.SpectralPowerDistribution('Carré 1', spd_dict_UFO3)

filename = ''.join([filepath,'GG-UFO_04_Under_SpectralDistribution',numpyExtension])
spd_dict_UFO4 = np.load(filename).item()
spdW_ref_UFO4 = colour.SpectralPowerDistribution('Entre les 2 UFO', spd_dict_UFO4)

#filename = ''.join([filepath,'GG_Cro.png'])
#print (filename)

#multi_spd_plot([spdW_ref_Sun, spdW_ref_UFO1, spdW_ref_UFO2, spdW_ref_UFO4, spdW_ref_UFO3], 
#               bounding_box=[380,780, 0, 4], title='GG Cro', y_label='Power Density (W.m-2.nm-1)',
#              fname=filename)

multi_spd_plot([spdW_ref_Sun, spdW_ref_UFO1, spdW_ref_UFO2, spdW_ref_UFO4, spdW_ref_UFO3], 
               bounding_box=[380,780, 0, 4], title='GG Cro', y_label='Power Density (W.m-2.nm-1)')



In [164]:
filename = ''.join([filepath,'GG-Flo_01_FS',numpyExtension])
spd_dict_Flo1 = np.load(filename).item()
spdW_ref_Flo1 = colour.SpectralPowerDistribution('Flo 1', spd_dict_Flo1)

filename = ''.join([filepath,'GG-Flo_02_FS',numpyExtension])
spd_dict_Flo2 = np.load(filename).item()
spdW_ref_Flo2 = colour.SpectralPowerDistribution('Flo 2', spd_dict_Flo2)

filename = ''.join([filepath,'GG-Flo_03_FS',numpyExtension])
spd_dict_Flo3 = np.load(filename).item()
spdW_ref_Flo3 = colour.SpectralPowerDistribution('Flo 3', spd_dict_Flo3)

#filename = ''.join([filepath,'GG_Flo.png'])
#print (filename)
#multi_spd_plot([spdW_ref_Sun, spdW_ref_Flo1, spdW_ref_Flo2, spdW_ref_Flo3], 
#               bounding_box=[380,780, 0, 1.5], title='GG Flo', y_label='Power Density (W.m-2.nm-1)',
#              fname=filename)

multi_spd_plot([spdW_ref_Sun, spdW_ref_Flo1, spdW_ref_Flo2, spdW_ref_Flo3], 
               bounding_box=[380,780, 0, 1.5], title='GG Flo', y_label='Power Density (W.m-2.nm-1)')



In [165]:
filename = ''.join([filepath,'FS-230W-45.7mV_01_Under_SpectralDistribution',numpyExtension])
spd_dict_FB1 = np.load(filename).item()
spdW_ref_FB1 = colour.SpectralPowerDistribution('Flo 1', spd_dict_FB1)


#filename = ''.join([filepath,'FB_Flo.png'])
#print (filename)

multi_spd_plot([spdW_ref_Sun, spdW_ref_FB1], 
               bounding_box=[380,780, 0, 1.5], title='FB Flo', y_label='Power Density (W.m-2.nm-1)')



In [166]:
filename = ''.join([filepath,'GG-Flo_04_CFL',numpyExtension])
spd_dict_Flo4 = np.load(filename).item()
spdW_ref_Flo4 = colour.SpectralPowerDistribution('CFL', spd_dict_Flo4)

filename = ''.join([filepath,'GG-Flo_05_HPS',numpyExtension])
spd_dict_Flo5 = np.load(filename).item()
spdW_ref_Flo5 = colour.SpectralPowerDistribution('HPS 1', spd_dict_Flo5)

filename = ''.join([filepath,'GG-Flo_08_HPS',numpyExtension])
spd_dict_Flo6 = np.load(filename).item()
spdW_ref_Flo6 = colour.SpectralPowerDistribution('HPS 2', spd_dict_Flo6)

#filename = ''.join([filepath,'CFL_HPS.png'])
#print (filename)

#multi_spd_plot([spdW_ref_Sun, spdW_ref_Flo1, spdW_ref_Flo2, spdW_ref_Flo3], 
#               bounding_box=[380,780, 0, 2.25], use_spds_colours=False, title='CFL and HPS', 
#               y_label='Power Density (W.m-2.nm-1)', fname=filename)

multi_spd_plot([spdW_ref_Sun, spdW_ref_Flo4, spdW_ref_Flo5, spdW_ref_Flo6], 
               bounding_box=[380,780, 0, 2.25], use_spds_colours=False, title='CFL and HPS', y_label='Power Density (W.m-2.nm-1)')



In [184]:
filename = ''.join([filepath,'GG-Flo_01_Douche',numpyExtension])
spd_dict_Douche= np.load(filename).item()
spdW_ref_Douche = colour.SpectralPowerDistribution('Douche', spd_dict_Douche)

filename = ''.join([filepath,'GG-Flo_02_Carre',numpyExtension])
spd_dict_Carre = np.load(filename).item()
spdW_ref_Carre = colour.SpectralPowerDistribution('Carré 2', spd_dict_Carre)



#filename = ''.join([filepath,'CFL_HPS.png'])
#print (filename)

#multi_spd_plot([spdW_ref_Sun, spdW_ref_Flo1, spdW_ref_Flo2, spdW_ref_Flo3], 
#               bounding_box=[380,780, 0, 2.25], use_spds_colours=False, title='CFL and HPS', 
#               y_label='Power Density (W.m-2.nm-1)', fname=filename)

multi_spd_plot([spdW_ref_Carre, spdW_ref_Douche,spdW_ref_UFO3], 
               bounding_box=[380,780, 0, 1], use_spds_colours=False, title='Douche et Carré', 
               y_label='Power Density (W.m-2.nm-1)')


Now let's take the Mc Cree curve as a luminous efficiency function.

Source :

McCREE, K. J., 1972. The action spectrum, absorptance and quantum yield of photosynthesis in crop plants. Agrie. MeteoroL, 9: 191-216. http://www.inda-gro.com/IG/sites/default/files/pdf/ACTION-SPECTRUM-KJMCCREE.pdf

--- excerpt ---

PHOTOSYNTHESIS IN CROP PLANTS 
...
RELATIVE ACTION OF FIELD PLANT SPECIES 

nm  1    3    4    6    7    8    18   20   Mean 
350 0.01 0.01 0.00 0.00 0.01 0.02 0.00 0.02 0.01
375 0.06 0.05 0.09 0.05 0.09 0.09 0.10 0.05 0.07
400 0.40 0.25 0.32 0.24 0.26 0.27 0.32 0.20 0.28
425 0.49 0.47 0.50 0.50 0.51 0.48 0.44 0.44 0.48
450 0.55 0.51 0.55 0.55 0.52 0.55 0.49 0.44 0.52
475 0.51 0.51 0.50 0.52 0.49 0.55 0.48 0.41 0.49
500 0.55 0.55 0.53 0.51 0.52 0.55 0.53 0.46 0.53
525 0.53 0.59 0.56 0.54 0.58 0.57 0.52 0.51 0.55
550 0.58 0.67 0.60 0.57 0.60 0.65 0.57 0.57 0.60
575 0.73 0.80 0.78 0.73 0.72 0.79 0.70 0.69 0.74
600 0.85 0.90 0.88 0.84 0.87 0.88 0.88 0.85 0.87
625 0.97 1.00 0.98 0.94 0.94 0.97 1.00 0.95 0.97
650 0.98 0.95 0.93 0.93 0.95 0.92 0.93 0.91 0.94
675 1.00 1.00 1.00 1.00 1.00 1.00 0.98 1.00 1.00
700 0.46 0.50 0.52 0.47 0.46 0.43 0.43 0.46 0.47
725 0.13 0.12 0.13 0.09 0.11 0.08 0.09 0.09 0.10
750 0.03 0.03 0.03 0.02 0.04 0.02 0.02 0.02 0.03 

THE AVERAGE PLANT 
...
The same basic shape of curve was encountered in all species of green plant
tested. The spectral quantum yield was always composed of three curves, with
peaks at 440, 620 and 670 nm, +/-10 nm. 
...

--- end of excerpt ---


In [168]:
# Action curve data from the paper cited above as a dictionary {wavelength,action}
relativeAction = {
350 : 0.01,
375 : 0.07,
400 : 0.28,
425 : 0.48,
450 : 0.52,
475 : 0.49,
500 : 0.53,
525 : 0.55,
550 : 0.60,
575 : 0.74,
600 : 0.87,
625 : 0.97,
650 : 0.94,
675 : 1.00,
700 : 0.47,
725 : 0.10,
750 : 0.03, 
775 : 0.01,   # This data point is not in the paper
800 : 0       # This data point is not in the paper
}

spd_action = colour.SpectralPowerDistribution('Relative Action', relativeAction)

#filename = ''.join([filepath,'Relative_Action.png'])
single_spd_plot(spd_action, title='Mc Cree Relative Action Curve', 
                y_label='A') #, fname=filename)



In [169]:
wavelength_380_780 = np.arange(380,781)
#value = np.interp(wavelength,np.array(list(relativeAction.keys())),np.array(list(relativeAction.values())))

relativeActionInterpolated=sp.interpolate.interp1d(np.array(list(relativeAction.keys())),
                              np.array(list(relativeAction.values())),
                             kind="cubic")(wavelength_380_780)

spd_action_interpolated = colour.SpectralPowerDistribution('Relative Action (Watt)',dict(zip(wavelength_380_780,relativeActionInterpolated)))
#filename = ''.join([filepath,'Relative_Action_Interpolated.png'])
single_spd_plot(spd_action_interpolated, title='Mc Cree Relative Action Curve (Watt weighted)', 
                y_label='A' ) #, fname=filename)



In [170]:
## Convert W to mole of photon / s
# E = hc/L where: 
#    E is the energy of a photon measured in Joules (J)
#    h is the Planck constant (6.626E-34 J*s) 
#    c is the speed of light (299792458 m/s) 
#    L is the wavelength of the photon measured in meters (m)
# Also we need Avagadro’s constant (A = 6.022 * 10^23)
watttomol = np.zeros(781-380)
for i in range(380,781,1):
    watttomol[i-380]=i*1e-9/(299792458*6.626E-34*6.022E23)

spd_watttomol = colour.SpectralPowerDistribution('Watt to micro mole', dict(zip(wavelength_380_780,watttomol/1000000)))
single_spd_plot(spd_watttomol, title='Watt to micro mol per second', 
                y_label='micro mol.s-1 for 1 Watt')



In [171]:
relativeActionPhotons = relativeActionInterpolated/watttomol
relativeActionPhotons = relativeActionPhotons / np.max(relativeActionPhotons)

spd_relativeActionPhotons = colour.SpectralPowerDistribution('Relative Action (Photon)'
                                                             ,dict(zip(wavelength_380_780,relativeActionPhotons)))
single_spd_plot(spd_relativeActionPhotons, title='Mc Cree Relative Action Curve (Photon weighted)', 
                y_label='A') #, fname=filename)


Now that we have our luminous efficiency function, lets look at some light power spectrum distribution through this function and lets compute something similar to Lux based on this function


In [172]:
# The sun measured at 102 000 Lux : 
spd_values = np.array(list(spd_dict_Sun.values())) * relativeActionInterpolated
spd = colour.SpectralPowerDistribution('Sun 102 000 Lux (Action)'
                                       ,dict(zip(wavelength_380_780,spd_values)))
single_spd_plot(spd, title='Sun 102 000 Lux (Action)', 
                y_label='A') #, fname=filename)



In [173]:
# Lets compute the area under this curve

print (np.sum(spd_values))


281.16959267

In [174]:
# Lets define the Pux, a fictional unit, as being the value of the area under the spectral action 
# curve scaled so that it is equal to 102000 for the sample power distribution of the sun measured at 102000 Lux

Pux = 102000 * np.sum(spd_values) / 281.16959267

print (Pux)


102000.0

In [175]:
# Now lets compute the Pux value of some other lights

filename = ''.join([filepath,'Sun-Calib-344mV_01_5119K_SpectralDistribution',numpyExtension])
spd_dict_Sun2 = np.load(filename).item()
spd_ref_Sun2 = colour.SpectralPowerDistribution('Sun 5x 000 Lux', spd_dict_Sun2)

#print("Ming Ben", round(102000 * np.sum(np.array(list(spd_dict_Ming.values())) * relativeActionInterpolated) / 281.16959267))
#print("FS Chanzon", round(102000 * np.sum(np.array(list(spd_dict_FSC.values())) * relativeActionInterpolated) / 281.16959267))
print("Sun 1 :", round(102000 * np.sum(np.array(list(spd_dict_Sun.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("Sun 2 :", round(102000 * np.sum(np.array(list(spd_dict_Sun2.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("UFO 1 :", round(102000 * np.sum(np.array(list(spd_dict_UFO1.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("UFO 2 :", round(102000 * np.sum(np.array(list(spd_dict_UFO2.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("Carré :", round(102000 * np.sum(np.array(list(spd_dict_UFO3.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("UFO 1/2 :", round(102000 * np.sum(np.array(list(spd_dict_UFO4.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("Flo 1 :", round(102000 * np.sum(np.array(list(spd_dict_Flo1.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("Flo 2 :", round(102000 * np.sum(np.array(list(spd_dict_Flo2.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("Flo 3 :", round(102000 * np.sum(np.array(list(spd_dict_Flo3.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("CFL 1 :", round(102000 * np.sum(np.array(list(spd_dict_Flo4.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("HPS 1 :", round(102000 * np.sum(np.array(list(spd_dict_Flo5.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("HPS 2 :", round(102000 * np.sum(np.array(list(spd_dict_Flo6.values())) * relativeActionInterpolated) / 281.16959267), "Pux")
print("FB  1 :", round(102000 * np.sum(np.array(list(spd_dict_FB1.values()))  * relativeActionInterpolated) / 281.16959267), "Pux")

print("Douche :", round(102000 * np.sum(np.array(list(spd_dict_Douche.values()))  * relativeActionInterpolated) / 281.16959267), "Pux")
print("Carré 2 :", round(102000 * np.sum(np.array(list(spd_dict_Carre.values()))  * relativeActionInterpolated) / 281.16959267), "Pux")


Sun 1 : 102000.0 Pux
Sun 2 : 56324.0 Pux
UFO 1 : 43189.0 Pux
UFO 2 : 31151.0 Pux
Carré : 10150.0 Pux
UFO 1/2 : 19765.0 Pux
Flo 1 : 37848.0 Pux
Flo 2 : 18866.0 Pux
Flo 3 : 28123.0 Pux
CFL 1 : 570.0 Pux
HPS 1 : 10575.0 Pux
HPS 2 : 31229.0 Pux
FB  1 : 16698.0 Pux
Douche : 7178.0 Pux
Carré 2 : 11936.0 Pux

In [182]:
# For reference here is the toal W/m2 of the same lights
print("Sun 1 :", round(np.sum(np.array(list(spd_dict_Sun.values())) ) ), "W/m2")
print("Sun 2 :", round(np.sum(np.array(list(spd_dict_Sun2.values())) ) ), "W/m2")
print("UFO 1 :", round(np.sum(np.array(list(spd_dict_UFO1.values())) ) ), "W/m2")
print("UFO 2 :", round(np.sum(np.array(list(spd_dict_UFO2.values())) ) ), "W/m2")
print("Carré :", round(np.sum(np.array(list(spd_dict_UFO3.values())) ) ), "W/m2")
print("UFO 1/2 :", round(np.sum(np.array(list(spd_dict_UFO4.values())) ) ), "W/m2")
print("Flo 1 :", round(np.sum(np.array(list(spd_dict_Flo1.values())) ) ), "W/m2")
print("Flo 2 :", round( np.sum(np.array(list(spd_dict_Flo2.values())) ) ), "W/m2")
print("Flo 3 :", round( np.sum(np.array(list(spd_dict_Flo3.values())) ) ), "W/m2")
print("CFL 1 :", round( np.sum(np.array(list(spd_dict_Flo4.values())) ) ), "W/m2")
print("HPS 1 :", round( np.sum(np.array(list(spd_dict_Flo5.values())) ) ), "W/m2")
print("HPS 2 :", round( np.sum(np.array(list(spd_dict_Flo6.values())) ) ), "W/m2")
print("FB  1 :", round( np.sum(np.array(list(spd_dict_FB1.values()))  ) ), "W/m2")

print("Douche :", round( np.sum(np.array(list(spd_dict_Douche.values()))  ) ), "W/m2")
print("Carre :", round( np.sum(np.array(list(spd_dict_Carre.values()))  ) ), "W/m2")


Sun 1 : 493.0 W/m2
Sun 2 : 276.0 W/m2
UFO 1 : 146.0 W/m2
UFO 2 : 105.0 W/m2
Carré : 42.0 W/m2
UFO 1/2 : 67.0 W/m2
Flo 1 : 143.0 W/m2
Flo 2 : 70.0 W/m2
Flo 3 : 106.0 W/m2
CFL 1 : 3.0 W/m2
HPS 1 : 39.0 W/m2
HPS 2 : 111.0 W/m2
FB  1 : 61.0 W/m2
Douche : 31.0 W/m2
Carre : 46.0 W/m2

In [177]:
## Extrapolating artificial lihts spectrograms to the same total light 
# power action as the full sun light at 102000 lux
sun_avg_action = np.sum(np.array(list(spd_dict_Sun.values())) * relativeActionInterpolated)
sun2_avg_action = np.sum(np.array(list(spd_dict_Sun2.values())) * relativeActionInterpolated)
sample_avg_action = np.sum(np.array(list(spd_dict_Flo1.values())) * relativeActionInterpolated)
extrapolated_sample=sun_avg_action*np.array(list(spd_dict_Flo1.values()))/sample_avg_action
spd_sample_ext = colour.SpectralPowerDistribution('Flo1 (Extrapolated to 102000 Lux Action)'
                                       ,dict(zip(wavelength_380_780,extrapolated_sample)))

extrapolated_sample2=sun2_avg_action*np.array(list(spd_dict_Flo1.values()))/sample_avg_action
spd_sample2_ext = colour.SpectralPowerDistribution('Flo1 (Extrapolated to 5x000 Lux Action)'
                                       ,dict(zip(wavelength_380_780,extrapolated_sample2)))

print("Electrical power to have 102 k lux sun equivalent action at high spots:")
print(round(500*sun_avg_action/(sample_avg_action*1.6)),"W/m2")

print("Electrical power to have 50 k lux sun equivalent action at low spots:")
print(round(500*sun2_avg_action/(sample_avg_action*1.6)),"W/m2")

multi_spd_plot([spdW_ref_Sun, spd_sample_ext, spd_ref_Sun2, spd_sample2_ext, spdW_ref_Flo1],
               bounding_box=[380,780, 0, 5], use_spds_colours=False, 
               title='xxx', y_label='Power Density (W.m-2.nm-1)')


Electrical power to have 102 k lux sun equivalent action at high spots:
842.0 W/m2
Electrical power to have 50 k lux sun equivalent action at low spots:
465.0 W/m2

In [178]:
## Extrapolating artificial lihts spectrograms to the same total light 
# power action as the full sun light at 102000 lux
sun_avg_action = np.sum(np.array(list(spd_dict_Sun.values())) * relativeActionInterpolated)
sun2_avg_action = np.sum(np.array(list(spd_dict_Sun2.values())) * relativeActionInterpolated)
sample_avg_action = np.sum(np.array(list(spd_dict_UFO1.values())) * relativeActionInterpolated)
extrapolated_sample=sun_avg_action*np.array(list(spd_dict_UFO1.values()))/sample_avg_action
spd_sample3_ext = colour.SpectralPowerDistribution('UFO1 (Extrapolated to 102000 Lux Action)'
                                       ,dict(zip(wavelength_380_780,extrapolated_sample)))

extrapolated_sample2=sun2_avg_action*np.array(list(spd_dict_UFO1.values()))/sample_avg_action
spd_sample4_ext = colour.SpectralPowerDistribution('UFO1 (Extrapolated to 5x000 Lux Action)'
                                       ,dict(zip(wavelength_380_780,extrapolated_sample2)))

print("Electrical power to have 102 k lux sun equivalent action at high spots:")
print(round(90*sun_avg_action/(sample_avg_action*0.25)),"W/m2")

print("Electrical power to have 50 k lux sun equivalent action at high spots:")
print(round(90*sun2_avg_action/(sample_avg_action*0.25)),"W/m2")

multi_spd_plot([spdW_ref_Sun, spd_sample3_ext, spd_ref_Sun2, spd_sample4_ext, spdW_ref_UFO1],
               bounding_box=[380,780, 0, 10], use_spds_colours=False, 
               title='xxx', y_label='Power Density (W.m-2.nm-1)')


Electrical power to have 102 k lux sun equivalent action at high spots:
850.0 W/m2
Electrical power to have 50 k lux sun equivalent action at high spots:
469.0 W/m2

In [179]:
# Load the data from some files that have been generated by the previous cell
chartList = {
0: "LED-Red-3A-1m_01_Under_SpectralDistribution",
1000: "LED-Red-3A-1m_02_Under_SpectralDistribution",
900: "LED-Red-3A-1m_03_Under_SpectralDistribution",
800: "LED-Red-3A-1m_04_Under_SpectralDistribution",
 500: "LED-Red-3A-1m_05_Under_SpectralDistribution",
200: "LED-Red-3A-1m_06_Under_SpectralDistribution",
100: "LED-Red-3A-1m_07_Under_SpectralDistribution",
0: "LED-Red-3A-1m_08_Under_SpectralDistribution",
550: "LED-Red-3A-1m_09_Under_SpectralDistribution",
580: "LED-Red-3A-1m_10_Under_SpectralDistribution"
}

measureList = {}

for key,chartName in chartList.items():
    filename = ''.join([filepath,chartName,numpyExtension])
    spd_dict = np.load(filename).item()
    #spdW = colour.SpectralPowerDistribution('CFL', spd_dict)
    measureList[key]=np.sum(np.array(list(spd_dict.values())) )

spatialDistribution = np.zeros(30)

measureList


Out[179]:
{0: 6.8173519976669583,
 100: 6.3180956547098281,
 200: 5.5458967629046372,
 500: 3.8020530766987464,
 550: 3.4868416447944006,
 580: 3.2366345873432492,
 800: 1.7867220764071159,
 900: 1.5580198308544766,
 1000: 1.1809740449110531}

In [180]:
lists = sorted(measureList.items()) # sorted by key, return a list of tuples

x, y = zip(*lists) # unpack a list of pairs into two tuples

plt.plot(x, y)
#plt.show()


plt.plot()


Out[180]:
[]

In [181]:
1.5/6.8


Out[181]:
0.22058823529411764