Convert spectrum to Color (Python)

358 Views Asked by At

I developed a code to convert a reflectance spectrum to a color with LAB values:

import numpy as np
from colour import SpectralDistribution, XYZ_to_Lab, sd_to_XYZ

# Input the given data
wavelengths = np.arange(440, 1821, 20)
reflectance = ynp.array([0.02033, 0.0231, 0.00721, 0.00116, 0.00797, 0.0289, 0.05816, 0.08086, 0.08994, 0.05639, 0.03445, 0.03571, 0.09977, 0.22134, 0.26598, 0.27834, 0.2801, 0.28464, 0.28439, 0.27859, 0.26321, 0.2569, 0.25816, 0.2506, 0.24, 0.23622, 0.22916, 0.21983, 0.21327, 0.20822, 0.20621, 0.20368, 0.20444, 0.20696, 0.2115, 0.21529, 0.21831, 0.22008, 0.21932, 0.2168, 0.19738, 0.18099, 0.17191, 0.16409, 0.17645, 0.18906, 0.19107, 0.18981, 0.18275, 0.16838, 0.15324, 0.13256, 0.08666, 0.04555, 0.03143, 0.02966, 0.03269, 0.04051, 0.05211, 0.06447, 0.07808, 0.08767, 0.09599, 0.10255, 0.10709, 0.10911, 0.10431, 0.08868, 0.06068, 0.05539])

# Calculate the tristimulus values from the spectral data
sd = SpectralDistribution(reflectance, wavelengths)
xyz = sd_to_XYZ(sd)

# Convert the XYZ values to LAB
lab = XYZ_to_Lab(xyz/100)

# Print the resulting LAB values
print("The resulting LAB values:", lab)

So far my code worked perfectly fine, from the datasets I tried I received the colors I expected.

But now there is one dataset which gives me brownish tone rather than something red (see input values above): LAB = [ 26.01444853 15.31231523 21.21644098]

I suspect the mistake could be that the other datasets I used were normalized by the authors of the papers I took them from.

So I also tried to normalize my values with this formula: (X-min(X))/(max(X)-min(X))

But my results were [ 47.43 23.55 33.01], which is a brighter but still brownish tone.

Do you have any idea what else I can do? Any other formulas how to standardize values? Or do you think the mistake comes from my code?

Just as an example, this is one of the other datasets I completed successfully with my code (and the colour was exactly the way I expected it):

wavelengths = np.arange(440, 801, 5)

np.array([0.03376, 0.07618, 0.12939, 0.14376, 0.15309, 0.15235, 0.14153, 0.1336, 0.12566, 0.11916, 0.10835, 0.10185, 0.09104, 0.08814, 0.08596, 0.08306, 0.08088, 0.07942, 0.07939, 0.08009, 0.07935, 0.07645, 0.07211, 0.07137, 0.07062, 0.06772, 0.06339, 0.06264, 0.06262, 0.06548, 0.06617, 0.06543, 0.06541, 0.06826, 0.07903, 0.09052, 0.1092, 0.14442, 0.17318, 0.20984, 0.23931, 0.26375, 0.28675, 0.3119, 0.3277, 0.34207, 0.355, 0.36504, 0.37797, 0.39378, 0.41677, 0.43185, 0.44406, 0.4577, 0.47063, 0.47996, 0.48815, 0.49493, 0.49853, 0.50531, 0.50936, 0.51251, 0.51295, 0.51292, 0.5061, 0.49792, 0.49201, 0.48383, 0.46704, 0.46883, 0.47062, 0.46425, 0.4597, 0.46964, 0.46011, 0.45193, 0.45009, 0.44146, 0.43192, 0.42374, 0.41647])

Result: [ 42.86401144  43.94910661   8.95057537]
1

There are 1 best solutions below

0
Kel Solaar On

Because you compute the reflectance of a sample, you need to pass and illuminant to the spectral to tristimulus conversion routine, e.g. colour.SDS_ILLUMINANTS["D65"]:

import numpy as np

import colour.plotting
from colour import SpectralDistribution, XYZ_to_Lab, sd_to_XYZ

# Input the given data
wavelengths = np.arange(440, 1821, 20)
reflectance = np.array([0.02033, 0.0231, 0.00721, 0.00116, 0.00797, 0.0289, 0.05816, 0.08086, 0.08994, 0.05639, 0.03445, 0.03571, 0.09977, 0.22134, 0.26598, 0.27834, 0.2801, 0.28464, 0.28439, 0.27859, 0.26321, 0.2569, 0.25816, 0.2506, 0.24, 0.23622, 0.22916, 0.21983, 0.21327, 0.20822, 0.20621, 0.20368, 0.20444, 0.20696, 0.2115, 0.21529, 0.21831, 0.22008, 0.21932, 0.2168, 0.19738, 0.18099, 0.17191, 0.16409, 0.17645, 0.18906, 0.19107, 0.18981, 0.18275, 0.16838, 0.15324, 0.13256, 0.08666, 0.04555, 0.03143, 0.02966, 0.03269, 0.04051, 0.05211, 0.06447, 0.07808, 0.08767, 0.09599, 0.10255, 0.10709, 0.10911, 0.10431, 0.08868, 0.06068, 0.05539])

# Calculate the tristimulus values from the spectral data
sd = SpectralDistribution(reflectance, wavelengths)
colour.plotting.plot_single_sd(sd)
xyz = sd_to_XYZ(sd, illuminant=colour.SDS_ILLUMINANTS["D65"]) / 100
colour.plotting.plot_single_colour_swatch(colour.XYZ_to_sRGB(xyz))

# Convert the XYZ values to LAB
lab = XYZ_to_Lab(xyz)

# Print the resulting LAB values
print("The resulting LAB values:", lab)

Looking at the reflectance curve, between 10-20% in the red sensitivity region: Reflectance

This sRGB rendering looks correct to me:

sRGB