nifty.cl.spectrum_to_rgb module#
- class ColorSpaceTools[source]#
Bases:
objectStatic helper methods for color space conversions and CIE standard data.
- classmethod LMS_to_XYZ(LMS_values)[source]#
Transform LMS retinal cone response values to CIE 1931 XYZ tristimulus values.
- classmethod XYZ_to_LMS(XYZ_values)[source]#
Transform CIE 1931 XYZ tristimulus values to LMS retinal cone response values.
- static XYZ_to_xyY(XYZ_values)[source]#
Convert CIE 1931 XYZ to chromaticity-luminance xyY.
Undefined (returns zeros) for black pixels where sum(XYZ) = 0.
- classmethod embed_XYZ_perceived_color_in_sRGB(XYZ_values)[source]#
Transform CIE 1931 XYZ tristimulus values to sRGB, clipped to [0, 1].
- Parameters:
XYZ_values (numpy.ndarray) – XYZ tristimulus values, last axis has length 3.
- Returns:
sRGB values in [0, 1], same shape as input.
- Return type:
- static enhance_sRGB_color_contrast(sRGB_data, color_contrast_multiplier)[source]#
Enhance color saturation of an sRGB image while preserving black and white points.
- Parameters:
sRGB_data (numpy.ndarray) – sRGB values in [0, 1].
color_contrast_multiplier (float) – Saturation scale factor. 1.0 returns the input unchanged.
- Returns:
Contrast-enhanced sRGB values in [0, 1].
- Return type:
- classmethod get_cie1931_standard_observer_XYZ_tristimulus_values(wavelengths)[source]#
Return CIE 1931 XYZ tristimulus values for given wavelengths (nm).
Linearly interpolates the 5 nm-spaced standard observer table (380–780 nm). Out-of-range wavelengths are clamped to the table endpoints.
- Parameters:
wavelengths (numpy.ndarray) – Wavelengths in nm; any shape.
- Returns:
Shape
wavelengths.shape + (3,).- Return type:
- classmethod get_cie_d65_standard_illuminant_power(wavelengths)[source]#
Return relative spectral power of the CIE D65 illuminant at given wavelengths (nm).
- Parameters:
wavelengths (numpy.ndarray) – Wavelengths in nm; any shape.
- Returns:
Relative power values, same shape as input.
- Return type:
- class SpectrumToRGBProjector(flux_convention, spectral_axis_type, visible_bin_width, wavelength_min_mappable=440.0, wavelength_max_mappable=640.0)[source]#
Bases:
objectProject fields with a spectral dimension to sRGB color space.
The spectral dimension is mapped onto the visible light spectral range, converted to perceived colors following the CIE 1931 model of human color perception, and encoded into sRGB.
Setup (must be called before projecting):
Specify input bin boundaries via
specify_input_spectrum_bins_via_bin_boundaries()orspecify_input_spectrum_bins_via_center_and_width().Optionally fix the displayed luminance range via
set_luminance_range(), and optionally enable log compression viause_log_compression().
The display transform is fully described by two luminance values \((Y_\mathrm{black}, Y_\mathrm{saturation})\), a tone curve (linear or logarithmic) and a highlight policy. Physical quantities are turned into luminances by the converter methods
luminance_of_spectrum()andluminance_quantiles(), whose results are handed toset_luminance_range().The input spectral range is mapped onto the configured window
[wavelength_min_mappable, wavelength_max_mappable], which is a display choice and carries no physical wavelength meaning. Within that window the placement is affine — in the input coordinate forvisible_bin_width='proportional', in the bin index for'uniform'— so the assigned wavelengths are not related to the input by any physical law.spectral_axis_typefixes only which end of the window the low-input bins land on.- Parameters:
flux_convention ({'bin_integrated_flux', 'flux_density'}) –
Physical convention of all data passed to this projector — both the images handed to
project()and the spectra handed to the luminance converters.'bin_integrated_flux': values are total fluxes \(\Phi_k\), already integrated over their spectral bin.'flux_density': values are densities \(\partial\Phi/\partial\left[E \mid \lambda\right]\), which get multiplied by the bin widths internally.
The chosen convention is logged at construction time, because silently reusing a projector on data of the other kind produces a plausible-looking but wrong image.
spectral_axis_type ({'energy', 'wavelength'}) –
Physical meaning of the input spectral coordinate; controls the mapping direction only, never its spacing.
'energy': direction-inverting — the lowest input bin is placed atwavelength_max_mappable(red end) and the highest atwavelength_min_mappable(blue end). Only the ordering agrees with E = hc/λ; the spacing does not, since λ decreases affinely in E rather than as 1/E. Suited to energy- or frequency-domain data.'wavelength': direction-preserving — the lowest input bin is placed atwavelength_min_mappable(blue end) and the highest atwavelength_max_mappable(red end). Note that the input wavelengths are still rescaled onto the configured window, not used as they are.
visible_bin_width ({'uniform', 'proportional'}) –
How the visible wavelength width is distributed across input bins.
'uniform': every input bin receives the same visible width Δλ = (λ_max − λ_min) / N, regardless of its width in the input domain. Ideal for energy-domain data (especially geomspace bins) where equal visual treatment of each bin is desired.'proportional': each bin’s visible width is proportional to its width in the input domain, preserving relative spectral coverage. Ideal for wavelength-domain data.
wavelength_min_mappable (float) – Short-wavelength limit (nm) of the visible range to map onto. The highest input bin maps to this wavelength. Default: 440 nm.
wavelength_max_mappable (float) – Long-wavelength limit (nm) of the visible range to map onto. The lowest input bin maps to this wavelength. Default: 640 nm.
- __init__(flux_convention, spectral_axis_type, visible_bin_width, wavelength_min_mappable=440.0, wavelength_max_mappable=640.0)[source]#
- apply_color_map_ticks(ax, axis='y', n_ticks=5)[source]#
Set round-numbered flux ticks on a matplotlib axis showing a colour map.
Reads the image extent back off
ax, so whateverextentandoriginwere used when displaying the colour map are respected.- Parameters:
ax (matplotlib.axes.Axes) – Axis that the colour map image was drawn into.
axis ({'y', 'x'}) – Which axis the level (flux) dimension was drawn along. Default: ‘y’.
n_ticks (int) – Desired number of ticks. Default: 5.
- property flux_convention#
Physical convention of the data this projector expects.
- get_color_map_image(levels=None, n_levels=128)[source]#
Return an sRGB image showing the color assigned to each spectral bin.
Produces a 2-D image of shape
(n_levels, n_bins, 3). Rowi, columnkshows how a monochromatic spectrum carrying fluxlevels[i]in binkalone is rendered — nothing more and nothing less.Note that no per-column rescaling takes place: the luminance produced per unit flux varies by roughly a factor of ten across the visible range, so a mid-spectrum column genuinely is much brighter than a column at either end at the same flux, and columns reach full brightness at different heights. That is what the projection does, so that is what this legend shows.
The tone curve is taken from the most recent
project()call (falling back to an explicitly set luminance range), which is what keeps this legend consistent with the image it accompanies.- Parameters:
levels (array-like, 1-D, or None) – Flux levels to sample, in this projector’s
flux_convention.None(default) spans the flux range of the most recently projected data withn_levelssamples, and warns — the resulting levels are rarely the round, domain-appropriate numbers a reader wants.n_levels (int) – Number of rows generated when
levelsis None. Default: 128.
- Returns:
Shape
(n_levels, n_bins, 3)of sRGB values in [0, 1].- Return type:
- get_color_map_ticks(n_ticks=5, extent=None)[source]#
Return tick positions and labels for the last generated colour map.
Picks round flux values inside the range of the levels most recently passed to (or generated by)
get_color_map_image()and returns where they sit along the level axis.n_ticksis a target, not a guarantee: round numbers do not generally land exactlyn_tickstimes in a given range.- Parameters:
n_ticks (int) – Desired number of ticks. Default: 5.
extent (pair of float or None) – Data coordinates of the first and last level of the colour map image.
None(default) assumes[0, n_levels-1], i.e. pixel indices.
- Returns:
Tick positions in data coordinates, and their formatted labels.
- Return type:
(numpy.ndarray, list of str)
- luminance_of_spectrum(spectrum)[source]#
Return the luminance Y that a given spectrum is rendered with.
The spectrum is interpreted according to this projector’s
flux_convention. This is the intended way to turn a physical specification into the luminance values understood byset_luminance_range(), e.g.:# saturation luminance: a flat spectrum carrying a total flux of F widths = ... # the bin widths handed to specify_input_spectrum_bins_* flat = F*widths/widths.sum() # 'bin_integrated_flux' convention flat = np.full(n_bins, F/widths.sum()) # 'flux_density' convention proj.set_luminance_range(Y_saturation=proj.luminance_of_spectrum(flat))
- Parameters:
spectrum (array-like) – Spectrum with the spectral axis last; leading axes are allowed.
- Returns:
Luminance of each spectrum.
- Return type:
float or numpy.ndarray
- luminance_quantiles(data, q=(0.01, 0.99))[source]#
Return luminance quantiles of a sample spatio-spectral image.
Every pixel of
datais converted to a luminance, and the requested quantiles of the resulting distribution are returned. This is the natural way to pick a display range: it asks the data where its interesting luminances lie rather than requiring the caller to guess absolute values.Non-positive luminances (zero or negative) are excluded before quantiling. Masked or zero-padded fields are frequently majority-zero, which would otherwise pin the lower quantile to exactly zero and make log compression fail far away from its cause.
- Parameters:
data (array-like) – Spatio-spectral data with the spectral axis last, interpreted according to this projector’s
flux_convention.q (pair of float) –
Lower and upper quantile in [0, 1]. Default: (0.01, 0.99).
Note that log rendering usually wants a considerably higher lower quantile (~0.3-0.5, near the background level of the image) than the default, which is chosen to be unsurprising for linear rendering.
- Returns:
Luminance at the lower and upper quantile.
- Return type:
(float, float)
- property luminance_range#
(Y_black, Y_saturation) pair, or None if it was never set.
- project(data, XYZ_inspect_callback=None)[source]#
Project spatio-spectral data to sRGB perceived colors.
The data is interpreted according to this projector’s
flux_convention.- Parameters:
data (array-like) – Data with the spectral axis last.
XYZ_inspect_callback (callable or None) – Called as
fn(XYZ_raw, XYZ_tone_mapped)for inspection. Default: None.
- Returns:
sRGB values in [0, 1]; last axis has length 3.
- Return type:
- set_luminance_range(*, Y_saturation, Y_black=None, dynamic_range=None, highlights='clamp')[source]#
Fix the luminance range that is mapped onto the displayable range.
Luminance
Y_blackis rendered black, luminanceY_saturationis rendered at full display brightness, and the tone curve in between is linear unlessuse_log_compression()has been enabled.Note that
Y_saturationis deliberately not called a white point: a pixel reaching it is rendered at maximum brightness for its own chromaticity, which is white only if that chromaticity happens to be the D65 white point.Y_blackis unconditional by contrast, since zero luminance is black for every chromaticity.The arguments are keyword-only on purpose:
luminance_quantiles()returns(low, high)while this method readsY_saturationfirst, so a positional API would makeset_luminance_range(*proj.luminance_quantiles(x))silently swap the two ends of the range.- Parameters:
Y_saturation (float) – Luminance rendered at full display brightness. Typically obtained from
luminance_of_spectrum()orluminance_quantiles().Y_black (float or None) – Luminance rendered black.
None(default) means no black point, i.e. 0. Mutually exclusive withdynamic_range.dynamic_range (float or None) – Alternative way to place the black point, as
Y_saturation/dynamic_range. Mutually exclusive withY_black.highlights ({'clamp', 'clip_channels'}) –
What happens above
Y_saturation.'clamp'(default): luminance is clamped, which preserves chromaticity — over-bright pixels keep their hue and lose detail.'clip_channels': luminance is left unclamped and the individual sRGB channels clip independently, which shifts hue as a pixel blows out, the way a camera sensor does.
- specify_input_spectrum_bins_via_bin_boundaries(lower, upper)[source]#
Specify input spectral bins by their lower and upper boundaries.
Discards any luminance range, tone curve state and cached colour map, since those are only meaningful relative to a fixed set of bins.
- Parameters:
lower (array-like) – Lower boundary of each bin.
upper (array-like) – Upper boundary of each bin (must match length of lower).
- specify_input_spectrum_bins_via_center_and_width(centers, widths)[source]#
Specify input bins by their center coordinates and widths.
- Parameters:
centers (array-like) – Center coordinate of each bin.
widths (array-like) – Width of each bin (center ± width/2).
- use_log_compression(enabled=True)[source]#
Switch logarithmic compression of the luminance range on or off.
With log compression the tone curve is
log(Y/Y_black)/log(Y_saturation/Y_black)instead of the linear(Y - Y_black)/(Y_saturation - Y_black). A black point is then mandatory; projecting without one raises.