arrayfire / forge

High Performance Visualization
226 stars 48 forks source link

Colormap fixes #123

Closed 9prady9 closed 7 years ago

9prady9 commented 7 years ago

Fixes #122

kryptan commented 7 years ago

To compute sRGB color for a particular wavelength I would use the folllowing method:

  1. Calculate coordinates in the CIE XYZ colorspace for this wavelength. The method for computing XYZ values from a spectral radiance is described in Wikipedia. In our case spectral radiance is simply a Dirac delta function centered at the wavelength in question and all these integrals become equal to the color matching functions at this wavelength.
  2. Convert to linear sRGB.
  3. At least one of the R, G or B will be negative because this color will be outside of sRGB. The best thing to do here is probably to replace negative values with zero.

Once we've computed linear sRGB colors for each wavelength that we are interested in we need to normalize colors so that they will fit in 0-1 range (i.e. simply divide them by the maximum value).

Now we can apply the gamma to these colors get the proper sRGB.

kryptan commented 7 years ago

Google directed me to this weird website for the table values of color matching functions for the standard observer.

That's what I got:

spectrum

The range is 390 - 830 nm.

I used the following Mathematica code to produce the image above.

fs = Import[
   FileNameJoin[{NotebookDirectory[], "./lin2012xyz2e_5_7sf.csv"}]];
fs = Transpose[fs];
mf = fs[[2 ;; 4]];
srgb = {{3.2406, -1.5372, -0.4986}, {-0.9689, 1.8758, 
      0.0415}, {0.0557, -0.2040, 1.0570}}.mf/2.7;
srgb = Map[Max[0, #] &, srgb, {2}];
srgbNL = Map[#^(1/2.2) &, srgb, {2}];
spectrum = Map[ListInterpolation, srgbNL];
n = 500;
Image[Table[{spectrum[[1]][1 + i*88/n], spectrum[[2]][1 + i*88/n], 
   spectrum[[3]][1 + i*88/n]}, {j, 1, 100}, {i, 0, n}]]
9prady9 commented 7 years ago

The one i have been referring to is this, which computes the rgb values for 380nm-780nm range. You can see the range of colors using that code at this page interactively.

kryptan commented 7 years ago

Yes, I've read those. The comment in the original FORTRAN program says:

The red, green and blue values (RGB) are assumed to vary linearly with wavelength.

But I don't think this is true. There are good mathematical models of human vision and one can precisely calculate sRGB color corresponding to any wavelength or to any combination of wavelengths without such assumptions.

9prady9 commented 7 years ago

@kryptan Thank you for the clarification. Took me some time to understand the Mathematica code stub you shared, but was finally able to create the following octave script to generate the visible spectrum colours in sRGB space. I think i got it correct this time, the colormap shows up as expected.

spectrum

lin2012xyz2e_5_7sf = csvread("lin2012xyz2e_1_7sf.csv");
lambda = transpose(lin2012xyz2e_5_7sf(:,1));
lms = transpose(lin2012xyz2e_5_7sf(:,2:4));
XYZ_to_sRGB = [3.2404542, -1.5371385, -0.4985314;
 -0.9692660, 1.8760108, 0.0415560;
 0.0556434, -0.2040259, 1.0572252];
lrgb = XYZ_to_sRGB * lms;
crgb = lrgb / max(max(lrgb));
crgb = (crgb > 0.0) .* crgb;
bt = (abs(crgb) <= 0.0031308)*12.92.*abs(crgb);
at = (abs(crgb) > 0.0031308).*(1.055*(abs(crgb).^(1/2.4))-0.055);
srgb = bt+at;

wvl_2s = 390:1.72:830;

rInter = spline(lambda, srgb(1, :), wvl_2s);
gInter = spline(lambda, srgb(2, :), wvl_2s);
bInter = spline(lambda, srgb(3, :), wvl_2s);

rgb = [rInter; gInter; bInter];

rgb = (rgb > 0.0) .* rgb;
9prady9 commented 7 years ago

@arrayfire/core-devel I think this is ready for merge, pls review.

shehzan10 commented 7 years ago

Looks good to me except for changing Colors->Rainbow. I'm not against rainbow. It's whether changing the enum makes a significant difference to cause problems to users.

9prady9 commented 7 years ago

The reason i changed it is to make sense of the colormap that enum value represents. Colors was too generic to describe anything at all.

shehzan10 commented 7 years ago

The reason i changed it is to make sense of the colormap that enum value represents. Colors was too generic to describe anything at all.

I understand the reasoning and agree with it. That's why I'm not against the name itself.

9prady9 commented 7 years ago

As far as ArrayFire-Forge inter-operation, we should not see any issues as the enum value itself didn't change. Since forge 1.0 is not released, we can change this name without any repercussions.