-
for example, function visible_spectrum_plot() renders the visible spectrum on sRGB values, there are 5 discontinuities of RGB values on the figure, which locates at around 460nm, 465nm, 520nm, 550nm and 610nm respectively. The discontinuity locates at the intersection of spectrum locus and the extension line of sRGB triangle. I think it belongs to the problem of gamut mapping, that is the gamut of spectrum locus is out of the gamut of sRGB. When XYZ_2_sRGB() is called for the spectrum colors, negative numbers will switch to another channel suddenly at 5 locations. I have tried to employ gamut mapping method on CIELAB color space to render the spectrum colors. The workflow is below: spectrum XYZ -> Lab -> hold the hue angle to gamut mapping -> HSV -> sRGB. For simplity, S and V in HSV is fixed as 1 which need to be imporved. The results are good for long waveleths but not very well for short wavelength. Anyway maybe it is not a very important problem. |
Beta Was this translation helpful? Give feedback.
Replies: 33 comments
-
Yes it is definitely related to the fact that sRGB / Rec. 709 colourspaces cannot encode all the visible light possible values. We are for now clipping any values not in [0, 1] domain however it would be definitely interesting to have an option to have a remapping / compression of the colours. #154 is related and I wanted to explore rendering intent: http://dba.med.sc.edu/price/irf/Adobe_tg/manage/renderintent.html |
Beta Was this translation helpful? Give feedback.
-
Hi, Is any proper fix planned for the near future? In the meantime, could I get some "measure of wrongness", or a better (easy&fast) workaround suggestion? def single_spd_plot_desat(spd, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs):
"""
Show a single SPD plot with desaturated wavelegth colours underneath.
The colours are in a smooth transition.
(Hack by henczati)
Parameters
----------
spd : colour.SpectralPowerDistribution
SPD to plot.
cmfs : string
Colour matching functions definition name.
\*\*kwargs : \*\*
Keyword arguments.
"""
from colour import SpectralPowerDistribution
assert type(spd) is SpectralPowerDistribution
from colour.plotting import get_cmfs, single_spd_plot
import numpy as np
# TODO: Slow hack!
# get cmfs spd
cmfs_spd = get_cmfs(cmfs)
# backup original
cmfs_bkp = cmfs_spd.clone()
# modify
maxval = np.amax(cmfs_spd.values)
cmfs_spd * 0.7 + (0.3 * maxval)
# plot
single_spd_plot(spd, cmfs, **kwargs)
# restore original
cmfs_spd * 0 + cmfs_bkp
from colour.plotting import get_illuminant
single_spd_plot_desat(get_illuminant('d65')) |
Beta Was this translation helpful? Give feedback.
-
Hi, It is the same issue than @fangjy88 but actually even worse because there isn't any single colour in the spectral locus that can be represented correctly by sRGB colourspace, here is the truncated output of sRGB colour values for each line of this D65 plot:
Another way to see it: chromaticities outside the sRGB triangle cannot be represented correctly: What the API does so far is just normalising & clipping the displayed colours such as: colour.normalise([0.00145703, -0.00120632, 0.00836139])
# array([ 0.17425691, 0. , 1. ]) An alternative could be to use a wider gamut colourspace such as ACES2065-1: However the colours displayed would be inconsistent with your screen: |
Beta Was this translation helpful? Give feedback.
-
Thanks for the nicely aided explanation. Wouldn't it then be a more "acceptable" solution to uniformly scale the sRGB gamut triangle "out", keeping it's shape, without rotating it and keeping the whitepoint at the same place (or, respectively, "shrink" the colour space leaving the sRGB whitepoint in-place), so it would exactly envelop the whole spectral locus? If I understand it correctly, it would mean sacrificing colour depth (having a lot of the gamut unused) and also leading to desaturation, but would result in a continuous transition and colours that are closer in chroma to what they (theoretically) should be, wouldn't it?...well, if I'm correct, the pure spectral colours would be radially at the same place seen from the whitepoint (preserving hue), just now inside the gamut. |
Beta Was this translation helpful? Give feedback.
-
Yes there is no reason it wouldn't work, as you can see the ACES2065-1 plot is very smooth. I don't think that such a colourspace exists though, however we can certainly compute its primaries. |
Beta Was this translation helpful? Give feedback.
-
It would be great if you could add it, even if just through some optional parameter. |
Beta Was this translation helpful? Give feedback.
-
I have just computed an optimised set of primaries, I'll give a go at a rendering later today: [[ 1.91940636 0.33383091]
[ 0.25031545 1.65928545]
[-0.48604818 -0.99162364]] |
Beta Was this translation helpful? Give feedback.
-
Unsurprisingly (considering the massive gamut size) the rendering is quite dull and desaturated: |
Beta Was this translation helpful? Give feedback.
-
Yeah, now I see why nobody uses it. It is truly not as representative as I hoped. I miss the blues. :) Thanks for working it out anyway. I suspect that if we did not have the whitepoint fixed, and just matched the scaled unrotated triangle to best envelop the locus, all the colours would be shifted towards green, wouldn't they? And if we rotate or "distort" the gamut triangle, the hues of the spectral colours would get "remapped", too (as we've seen for the ACES2065-1 colourspace). I see that the overshoots/discontinuities in the current (default) plot are basically at wavelengths with colours corresponding to areas around the vertices of the gamut triangle. I wonder how it would look if you did something like only half the scale, when only a not too significant part of the "green lobe" would fall outside the gamut. I would expect a discontinuity in green, mainly at the 2 places where the locus goes outside the gamut, but maybe the difference in tangent of the true line of the locus and the line clipped by the gamut edge would not make that noticeable a break in colour continuity. Makes me wonder why I do not see the discontinuities on the chromaticity diagram itself, though. Probably my untrained eyes. |
Beta Was this translation helpful? Give feedback.
-
I hoped to have something like the spectrum in this image |
Beta Was this translation helpful? Give feedback.
-
The four kinds of rendering intents seem to be used in ICC profile, which usually work for gamut mappings from displays to printers. Unfortunately, I have not studied very deep in ICC profile. I guess that @henczati method of modifying CMFs actually mix 0.7 unit spectrum light and 0.3 unit reference white. Does it exactly reproduce the wiki image? |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
It looks like they did something similar. I wager that by looking at their grey background colour, it would be simple and straightforward to determine a ratio that would get very close to their image. |
Beta Was this translation helpful? Give feedback.
-
Wikipedia is usually painful because there is no way (that I'm aware of) to contact an author. However in that case the person pseudo seems to be his real identity: NickSpiker Googling for him yields the following article: http://irphotogirl.deviantart.com/journal/A-Full-Spectrum-Interview-Nick-Spiker-445834950 Looks like it is him to me, I'll try to contact him in order to know what he did. From the Wikipedia image description it looks like the spectrum has only been averaged with some gray:
|
Beta Was this translation helpful? Give feedback.
-
Thanks. |
Beta Was this translation helpful? Give feedback.
-
@fangjy88: The "continuity/smoothness properties" of the plots (in the "non-distorted" wavelength range) seem quite similar. |
Beta Was this translation helpful? Give feedback.
-
Actually yes I think so. |
Beta Was this translation helpful? Give feedback.
-
I got it I think: The fact you have a reddish background is because I wasn't doing chromatic adaptation from CIE Illuminant E to CIE Illuminant D Series D65 in the XYZ to sRGB conversion. Here is the code I used for the above figure (need to find out what gives the smoothest spectra while keeping the background to minimum now): def visible_spectrum_plot_smooth(cmfs='CIE 1931 2 Degree Standard Observer',
**kwargs):
"""
Plots the visible colours spectrum using given standard observer *CIE XYZ*
colour matching functions.
Parameters
----------
cmfs : unicode, optional
Standard observer colour matching functions used for spectrum creation.
\*\*kwargs : \*\*
Keywords arguments.
Returns
-------
bool
Definition success.
Examples
--------
>>> visible_spectrum_plot() # doctest: +SKIP
True
"""
cmfs = get_cmfs(cmfs)
cmfs = cmfs.clone().align(colour.DEFAULT_SPECTRAL_SHAPE)
cmfs += 0.5
wavelengths = cmfs.shape.range()
colours = colour.XYZ_to_sRGB(colour.wavelength_to_XYZ(wavelengths, cmfs),
colour.ILLUMINANTS['cie_2_1931']['E'])
colours = colour.normalise(colours)
settings = {
'title': 'The Visible Spectrum - {0}'.format(cmfs.title),
'x_label': 'Wavelength $\\lambda$ (nm)',
'x_tighten': True}
settings.update(kwargs)
return colour_parameters_plot([colour_parameter(x=x[0], RGB=x[1])
for x in tuple(zip(wavelengths, colours))],
**settings)
visible_spectrum_plot_smooth() |
Beta Was this translation helpful? Give feedback.
-
I just found this article: http://www.repairfaq.org/sam/repspec/, I haven't read it entirely but it seems to point to some addition done on the CMFS. |
Beta Was this translation helpful? Give feedback.
-
I discussed a bit with Nick, so apparently he is altering the RGB Linear values, by fiddling quickly I came up with that: and ultra smooth 10th of nanometer interpolated version: def visible_spectrum_plot_smooth(cmfs='CIE 1931 2 Degree Standard Observer',
**kwargs):
"""
Plots the visible colours spectrum using given standard observer *CIE XYZ*
colour matching functions.
Parameters
----------
cmfs : unicode, optional
Standard observer colour matching functions used for spectrum creation.
\*\*kwargs : \*\*
Keywords arguments.
Returns
-------
bool
Definition success.
Examples
--------
>>> visible_spectrum_plot() # doctest: +SKIP
True
"""
cmfs = get_cmfs(cmfs)
cmfs = cmfs.clone().align(colour.DEFAULT_SPECTRAL_SHAPE)
wavelengths = cmfs.shape.range()
oecf = colour.RGB_COLOURSPACES['sRGB'].transfer_function
colours = colour.XYZ_to_sRGB(colour.wavelength_to_XYZ(wavelengths, cmfs),
colour.ILLUMINANTS['cie_2_1931']['E'],
transfer_function=False)
colours = oecf(colour.normalise(colours + np.abs(np.min(colours))))
settings = {
'title': 'The Visible Spectrum - {0}'.format(cmfs.title),
'x_label': 'Wavelength $\\lambda$ (nm)',
'x_tighten': True}
settings.update(kwargs)
return colour_parameters_plot([colour_parameter(x=x[0], RGB=x[1])
for x in tuple(zip(wavelengths, colours))],
**settings)
visible_spectrum_plot_smooth() |
Beta Was this translation helpful? Give feedback.
-
Thanks, it looks You mentioned earlier that the chromatic adaptation was missing/incorrect, and that lead to a reddish tint in my plots. Do you think a similar issue might affect other computations, too? |
Beta Was this translation helpful? Give feedback.
-
This last version is not touching the CMFS at all, just acting on the final RGB values, (which I subjectively prefer).
It might in the case of the Smits (1999) notebook or other complex studies as chromatic adaptation is a very delicate subject and people sometimes don't agree if you need to apply it or not depending the context. I'm quite confident that for the core API it is fine. |
Beta Was this translation helpful? Give feedback.
-
Going back to Smits (1999) notebook after rerun it, I think I concluded back then that the differences were coming from the fact I'm interpolating the recovered spectral power distributions and it slightly changes the resulting tristimulus values. |
Beta Was this translation helpful? Give feedback.
-
That specifically interests me, as a main use I have for your package is to compare altered versions of SPDs (spectral data) with respect to colorimetric errors (i.e. how much difference it makes using one or the other). |
Beta Was this translation helpful? Give feedback.
-
@henczati : Let's continue the Smits (1999) discussion on #196 if you don't mind! :) |
Beta Was this translation helpful? Give feedback.
-
In latest develop branch https://github.com/colour-science/colour/:
I will tackle the diagrams later as I need to re-render the images. |
Beta Was this translation helpful? Give feedback.
-
Awesome, thanks. Will clone and try. |
Beta Was this translation helpful? Give feedback.
-
Great! @KelSolaar produced exactly as the Nick's image. However, in the http://www.repairfaq.org/sam/repspec/ @KelSolaar mentioned, Nick's highly saturated spectrum version is still discontinuous.(2 Degree-sRGB-False-Spectra-Direct-Conversion) |
Beta Was this translation helpful? Give feedback.
-
I suspect it is less obvious because of the high contrast ratio overtaking your ability to distinguish hue variations properly on his thin rendering, the fact it is presented with a white surround (browser / Github's page background) is not helping either. He has blurred the resulting spectrum too, I'm wondering if it's only a vertical directional blur or a regular square gaussian blur that will also blur the image horizontally. That would help with the discontinuities, although I'm not really into data creative adjustments for that kind of things. |
Beta Was this translation helpful? Give feedback.
-
I'm closing that discussion for now. Feel free to comment and I'll reopen it! :) |
Beta Was this translation helpful? Give feedback.
Yes it is definitely related to the fact that sRGB / Rec. 709 colourspaces cannot encode all the visible light possible values. We are for now clipping any values not in [0, 1] domain however it would be definitely interesting to have an option to have a remapping / compression of the colours. #154 is related and I wanted to explore rendering intent: http://dba.med.sc.edu/price/irf/Adobe_tg/manage/renderintent.html