I would like to convert a multispectral image to RGB. After parsing said image, its an ndarray of shape (w, h, c)
. where c is the hundred-or-so wavelengths which were measured. Null measurements are -999, not 0.
For a single pixel, it is easy to use the colour
library to use the "intensities" of each wavelength to recover the colour in sRGB:
import colour
pixel_intensities = {380: 0.048, 385: 0.051, 390: 0.055}
sd = colour.SpectralDistribution(pixel_intensities )
cmfs = colour.MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
illuminant = colour.SDS_ILLUMINANTS["D65"]
XYZ = colour.sd_to_XYZ(sd, cmfs, illuminant)
red, green, blue = colour.XYZ_to_sRGB(XYZ)
A naive solution for applying this to the entire image would be to go through each pixel and run it through the function one at a time. I found that it was very slow.
Another naive solution would be to cherry pick only three wavelengths: one for the red channel, one for the green channel, and one for the blue channel. This yielded decent results and executed almost instantly in comparison. However, I found that the colours were not very accurate.
Is there an alternative solution that also requires little code?
It is possible to integrate an Array directly but you need to pass the shape along side the array. We also recommend using colour.msds_to_XYZ
definition even though it uses colour.sd_to_XYZ
definition under the hood. This clarifies the intent! Finally, it is much faster to use the Integration
method:
import numpy as np
import colour.plotting
from colour import (
MSDS_CMFS,
SDS_ILLUMINANTS,
MultiSpectralDistributions,
SpectralShape,
msds_to_XYZ,
)
cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
illuminant = SDS_ILLUMINANTS["D65"]
shape = SpectralShape(400, 700, 60)
data = np.array(
[
[
0.0137,
0.0159,
0.0096,
0.0111,
0.0179,
0.1057,
0.0433,
0.0258,
0.0248,
0.0186,
0.0310,
0.0473,
],
[
0.0913,
0.3145,
0.2582,
0.0709,
0.2971,
0.4620,
0.2683,
0.0831,
0.1203,
0.1292,
0.1682,
0.3221,
],
[
0.0152,
0.0842,
0.4139,
0.0220,
0.5630,
0.1918,
0.2373,
0.0430,
0.0054,
0.0079,
0.3719,
0.2268,
],
[
0.0281,
0.0907,
0.2228,
0.1249,
0.2375,
0.5625,
0.0518,
0.3230,
0.0065,
0.4006,
0.0861,
0.3161,
],
[
0.1918,
0.7103,
0.0041,
0.1817,
0.0024,
0.4209,
0.0118,
0.2302,
0.1860,
0.9404,
0.0041,
0.1124,
],
[
0.0430,
0.0437,
0.3744,
0.0020,
0.5819,
0.0027,
0.0823,
0.0081,
0.3625,
0.3213,
0.7849,
0.0024,
],
]
)
msds = MultiSpectralDistributions(data, shape)
XYZ = msds_to_XYZ(msds, cmfs, illuminant, method="Integration") / 100
colour.plotting.plot_image(np.clip(colour.XYZ_to_sRGB(XYZ.reshape(3, 4, 3)), 0, 1))
[[ 0.0750297 0.03948784 0.08403467]
[ 0.26925968 0.15072461 0.28705781]
[ 0.16703219 0.28217235 0.25645598]
[ 0.11576701 0.08640099 0.06576841]
[ 0.18731479 0.35075036 0.30145727]
[ 0.45165676 0.39613692 0.4367835 ]
[ 0.0817557 0.13093418 0.25942094]
[ 0.22467629 0.19309908 0.07963755]
[ 0.06578124 0.02525535 0.11093077]
[ 0.43914736 0.27980392 0.11729266]
[ 0.08536592 0.19703017 0.17705093]
[ 0.23908825 0.26212953 0.30676315]]