def test_wavelength_to_XYZ(self): """ Tests :func:`colour.colorimetry.tristimulus.wavelength_to_XYZ` definition. """ np.testing.assert_almost_equal( wavelength_to_XYZ( 480, CMFS.get('CIE 1931 2 Degree Standard Observer')), np.array([0.09564, 0.13902, 0.81295]), decimal=7) np.testing.assert_almost_equal( wavelength_to_XYZ( 480, CMFS.get('CIE 2012 2 Degree Standard Observer')), np.array([0.08182895, 0.1788048, 0.7552379]), decimal=7) np.testing.assert_almost_equal( wavelength_to_XYZ( 641.5, CMFS.get('CIE 2012 2 Degree Standard Observer')), np.array([0.44575583, 0.18184213, 0.]), decimal=7)
def test_wavelength_to_XYZ(self): """ Test :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ` definition. """ np.testing.assert_almost_equal( wavelength_to_XYZ( 480, MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]), np.array([0.09564, 0.13902, 0.81295]), decimal=7, ) np.testing.assert_almost_equal( wavelength_to_XYZ( 480, MSDS_CMFS["CIE 2012 2 Degree Standard Observer"]), np.array([0.08182895, 0.17880480, 0.75523790]), decimal=7, ) np.testing.assert_almost_equal( wavelength_to_XYZ( 641.5, MSDS_CMFS["CIE 2012 2 Degree Standard Observer"]), np.array([0.44575583, 0.18184213, 0.00000000]), decimal=7, )
def test_n_dimensional_wavelength_to_XYZ(self): """ Tests :func:`colour.colorimetry.tristimulus.wavelength_to_XYZ` definition n-dimensional arrays support. """ cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer') wl = 480 XYZ = np.array([0.09564, 0.13902, 0.81295]) np.testing.assert_almost_equal( wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) wl = np.tile(wl, 6) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal( wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) wl = np.reshape(wl, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal( wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) wl = np.reshape(wl, (2, 3, 1)) XYZ = np.reshape(XYZ, (2, 3, 1, 3)) np.testing.assert_almost_equal( wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7)
def test_n_dimensional_wavelength_to_XYZ(self): """ Tests :func:`colour.colorimetry.tristimulus.wavelength_to_XYZ` definition n-dimensional arrays support. """ cmfs = CMFS['CIE 1931 2 Degree Standard Observer'] wl = 480 XYZ = np.array([0.09564, 0.13902, 0.81295]) np.testing.assert_almost_equal(wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) wl = np.tile(wl, 6) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal(wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) wl = np.reshape(wl, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal(wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) wl = np.reshape(wl, (2, 3, 1)) XYZ = np.reshape(XYZ, (2, 3, 1, 3)) np.testing.assert_almost_equal(wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7)
def test_n_dimensional_wavelength_to_XYZ(self): """ Test :func:`colour.colorimetry.tristimulus_values.wavelength_to_XYZ` definition n-dimensional arrays support. """ cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"] wl = 480 XYZ = wavelength_to_XYZ(wl, cmfs) wl = np.tile(wl, 6) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_almost_equal(wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) wl = np.reshape(wl, (2, 3)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_almost_equal(wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7) wl = np.reshape(wl, (2, 3, 1)) XYZ = np.reshape(XYZ, (2, 3, 1, 3)) np.testing.assert_almost_equal(wavelength_to_XYZ(wl, cmfs), XYZ, decimal=7)
def single_spd_plot(spd, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots given spectral power distribution. Parameters ---------- spd : SpectralPowerDistribution, optional Spectral power distribution to plot. cmfs : unicode Standard observer colour matching functions used for spectrum creation. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import SpectralPowerDistribution >>> data = {400: 0.0641, 420: 0.0645, 440: 0.0562} >>> spd = SpectralPowerDistribution('Custom', data) >>> single_spd_plot(spd) # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs shape = cmfs.shape spd = spd.clone().interpolate(shape) wavelengths = shape.range() colours = [] y1 = [] for wavelength, value in spd: XYZ = wavelength_to_XYZ(wavelength, cmfs) colours.append(XYZ_to_sRGB(XYZ)) y1.append(value) colours = normalise(colours) settings = { 'title': '"{0}" - {1}'.format(spd.name, cmfs.name), 'x_label': u'Wavelength λ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True, 'x_ticker': True, 'y_ticker': True } settings.update(kwargs) return colour_parameters_plot([ colour_parameter(x=x[0], y1=x[1], RGB=x[2]) for x in tuple(zip(wavelengths, y1, colours)) ], **settings)
def single_spd_plot(spd, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots given spectral power distribution. Parameters ---------- spd : SpectralPowerDistribution Spectral power distribution to plot. cmfs : unicode Standard observer colour matching functions used for spectrum creation. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import SpectralPowerDistribution >>> data = {400: 0.0641, 420: 0.0645, 440: 0.0562} >>> spd = SpectralPowerDistribution('Custom', data) >>> single_spd_plot(spd) # doctest: +SKIP True """ cmfs = get_cmfs(cmfs) shape = cmfs.shape spd = spd.clone().interpolate(shape, 'Linear') wavelengths = spd.wavelengths colours = [] y1 = [] for wavelength, value in spd: XYZ = wavelength_to_XYZ(wavelength, cmfs) colours.append(XYZ_to_sRGB(XYZ)) y1.append(value) colours = normalise(colours) settings = { 'title': '{0} - {1}'.format(spd.title, cmfs.title), 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True, 'x_ticker': True, 'y_ticker': True} settings.update(kwargs) return colour_parameters_plot( [colour_parameter(x=x[0], y1=x[1], RGB=x[2]) for x in tuple(zip(wavelengths, y1, colours))], **settings)
def visible_spectrum_plot(cmfs='CIE 1931 2 Degree Standard Observer', out_of_gamut_clipping=True, **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. out_of_gamut_clipping : bool, optional Out of gamut colours will be clipped if *True* otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. [1]_ \**kwargs : dict, optional Keywords arguments. Returns ------- Figure Current figure or None. Examples -------- >>> visible_spectrum_plot() # doctest: +SKIP """ cmfs = get_cmfs(cmfs) cmfs = cmfs.clone().align(DEFAULT_SPECTRAL_SHAPE) wavelengths = cmfs.shape.range() colours = XYZ_to_sRGB( wavelength_to_XYZ(wavelengths, cmfs), ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E'], apply_encoding_cctf=False) if not out_of_gamut_clipping: colours += np.abs(np.min(colours)) colours = DEFAULT_PLOTTING_ENCODING_CCTF(normalise_maximum(colours)) settings = { 'title': 'The Visible Spectrum - {0}'.format(cmfs.title), 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': False, 'x_tighten': True, 'y_ticker': False } settings.update(kwargs) return colour_parameters_plot([ ColourParameter(x=x[0], RGB=x[1]) for x in tuple(zip(wavelengths, colours)) ], **settings)
def visible_spectrum_plot(cmfs='CIE 1931 2 Degree Standard Observer', out_of_gamut_clipping=True, **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. out_of_gamut_clipping : bool, optional Out of gamut colours will be clipped if *True* otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. [1]_ \**kwargs : dict, optional Keywords arguments. Returns ------- Figure Current figure or None. Examples -------- >>> visible_spectrum_plot() # doctest: +SKIP """ cmfs = get_cmfs(cmfs) cmfs = cmfs.clone().align(DEFAULT_SPECTRAL_SHAPE) wavelengths = cmfs.shape.range() colours = XYZ_to_sRGB( wavelength_to_XYZ(wavelengths, cmfs), ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E'], apply_encoding_cctf=False) if not out_of_gamut_clipping: colours += np.abs(np.min(colours)) colours = DEFAULT_PLOTTING_ENCODING_CCTF(normalise_maximum(colours)) settings = { 'title': 'The Visible Spectrum - {0}'.format(cmfs.title), 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': False, 'x_tighten': True, 'y_ticker': False} settings.update(kwargs) return colour_parameters_plot([ColourParameter(x=x[0], RGB=x[1]) for x in tuple(zip(wavelengths, colours))], **settings)
def test_wavelength_to_XYZ(self): """ Tests :func:`colour.colorimetry.tristimulus.wavelength_to_XYZ` definition. """ np.testing.assert_almost_equal(wavelength_to_XYZ( 480, CMFS.get('CIE 1931 2 Degree Standard Observer')), np.array([0.09564, 0.13902, 0.81295]), decimal=7) np.testing.assert_almost_equal( wavelength_to_XYZ(480, CMFS.get('CIE 2012 2 Degree Standard Observer')), np.array([0.08182895, 0.1788048, 0.7552379]), decimal=7) np.testing.assert_almost_equal(wavelength_to_XYZ( 641.5, CMFS.get('CIE 2012 2 Degree Standard Observer')), np.array([0.44575583, 0.18184213, 0.]), decimal=7)
def add_rainbow(axis, wavelengths, values, opacity=100): # sanity check: if not hasattr(axis, 'plot') and not hasattr(axis, 'add_patch'): raise Exception("ERROR:\tFirst argument needs to have method \"plot\".") from colour.plotting import XYZ_to_plotting_colourspace, filter_cmfs, CONSTANTS_COLOUR_STYLE from colour.colorimetry import CCS_ILLUMINANTS, wavelength_to_XYZ from colour.utilities import first_item, normalise_maximum from matplotlib.patches import Polygon col_map_f = "CIE 1931 2 Degree Standard Observer" cmfs = first_item(filter_cmfs(col_map_f).values()) wlen_cmfs = [n for n in wavelengths if n > cmfs.shape.start and n < cmfs.shape.end] clr = XYZ_to_plotting_colourspace( wavelength_to_XYZ(wlen_cmfs, cmfs), CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E'], apply_cctf_encoding=False) clr = normalise_maximum(clr) clr = CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding(clr) polygon = Polygon( np.vstack([ [min(wavelengths), 0], np.array([wavelengths, values]).T.tolist(), [max(wavelengths), 0], ]), facecolor='none', edgecolor='none') axis.add_patch(polygon) if opacity < 100: padding = 0 else: padding = 0.1 for dom, col in [(wavelengths - padding, 'black'), (wlen_cmfs, clr)]: axis.bar( x=dom, height=max(values), width=1 + padding, color=col, align='edge', alpha=opacity/100, clip_path=polygon ) pass
def visible_spectrum_plot(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, name = get_cmfs(cmfs), cmfs cmfs = cmfs.clone().align(DEFAULT_SPECTRAL_SHAPE) wavelengths = cmfs.shape.range() colours = [] for i in wavelengths: XYZ = wavelength_to_XYZ(i, cmfs) colours.append(XYZ_to_sRGB(XYZ)) colours = np.array([np.ravel(x) for x in colours]) colours *= 1 / np.max(colours) colours = np.clip(colours, 0, 1) settings = { 'title': 'The Visible Spectrum - {0}'.format(name), 'x_label': u'Wavelength λ (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)
def find_colour_single(wl): import colour from colour.colorimetry import wavelength_to_XYZ if wl < 360 or wl > 830: RGB = (0, 0, 0) else: XYZ = wavelength_to_XYZ(wl) RGB = colour.XYZ_to_sRGB(XYZ) for i in range(0, 3, 1): if RGB[i] < 0: RGB[i] = 0 if RGB[i] > 1: RGB[i] = 1 return (RGB)
def visible_spectrum_plot(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(DEFAULT_SPECTRAL_SHAPE) wavelengths = cmfs.shape.range() colours = [] for i in wavelengths: XYZ = wavelength_to_XYZ(i, cmfs) colours.append(XYZ_to_sRGB(XYZ)) colours = np.array([np.ravel(x) for x in colours]) colours *= 1 / np.max(colours) colours = np.clip(colours, 0, 1) 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)
def test_wavelength_to_XYZ(self): """ Tests :func:`colour.colorimetry.tristimulus.wavelength_to_XYZ` definition. """ np.testing.assert_almost_equal( wavelength_to_XYZ( 480, CMFS.get('CIE 1931 2 Degree Standard Observer')), np.array([0.09564, 0.13902, 0.81295]), decimal=7) np.testing.assert_almost_equal( wavelength_to_XYZ( 480, CMFS.get('CIE 2012 2 Degree Standard Observer')), np.array([0.08182895, 0.1788048, 0.7552379]), decimal=7) np.testing.assert_almost_equal( wavelength_to_XYZ( 641.5, CMFS.get('CIE 2012 2 Degree Standard Observer')), np.array([0.44575583, 0.18184213, 0.]), decimal=7) np.testing.assert_almost_equal( wavelength_to_XYZ( 480.5, CMFS.get('CIE 2012 2 Degree Standard Observer'), 'Cubic Spline'), np.array([0.07773422, 0.18148028, 0.7337162]), decimal=7) np.testing.assert_almost_equal( wavelength_to_XYZ( 480.5, CMFS.get('CIE 2012 2 Degree Standard Observer'), 'Linear'), np.array([0.07779856, 0.18149335, 0.7340129]), decimal=7) np.testing.assert_almost_equal( wavelength_to_XYZ( 480.5, CMFS.get('CIE 2012 2 Degree Standard Observer'), 'Pchip'), np.array([0.07773515, 0.18148048, 0.73372294]), decimal=7)
def plot_single_sd(sd, cmfs='CIE 1931 2 Degree Standard Observer', out_of_gamut_clipping=True, modulate_colours_with_sd_amplitude=False, equalize_sd_amplitude=False, **kwargs): """ Plots given spectral distribution. Parameters ---------- sd : SpectralDistribution Spectral distribution to plot. out_of_gamut_clipping : bool, optional Whether to clip out of gamut colours otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. modulate_colours_with_sd_amplitude : bool, optional Whether to modulate the colours with the spectral distribution amplitude. equalize_sd_amplitude : bool, optional Whether to equalize the spectral distribution amplitude. Equalization occurs after the colours modulation thus setting both arguments to *True* will generate a spectrum strip where each wavelength colour is modulated by the spectral distribution amplitude. The usual 5% margin above the spectral distribution is also omitted. cmfs : unicode Standard observer colour matching functions used for spectrum creation. Other Parameters ---------------- \\**kwargs : dict, optional {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, Please refer to the documentation of the previously listed definitions. Returns ------- tuple Current figure and axes. References ---------- :cite:`Spiker2015a` Examples -------- >>> from colour import SpectralDistribution >>> data = { ... 500: 0.0651, ... 520: 0.0705, ... 540: 0.0772, ... 560: 0.0870, ... 580: 0.1128, ... 600: 0.1360 ... } >>> sd = SpectralDistribution(data, name='Custom') >>> plot_single_sd(sd) # doctest: +SKIP .. image:: ../_static/Plotting_Plot_Single_SD.png :align: center :alt: plot_single_sd """ _figure, axes = artist(**kwargs) cmfs = first_item(filter_cmfs(cmfs).values()) sd = sd.copy() sd.interpolator = LinearInterpolator wavelengths = cmfs.wavelengths[np.logical_and( cmfs.wavelengths >= max(min(cmfs.wavelengths), min(sd.wavelengths)), cmfs.wavelengths <= min(max(cmfs.wavelengths), max(sd.wavelengths)), )] values = sd[wavelengths] colours = XYZ_to_plotting_colourspace( wavelength_to_XYZ(wavelengths, cmfs), ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E'], apply_encoding_cctf=False) if not out_of_gamut_clipping: colours += np.abs(np.min(colours)) colours = normalise_maximum(colours) if modulate_colours_with_sd_amplitude: colours *= (values / np.max(values))[..., np.newaxis] colours = COLOUR_STYLE_CONSTANTS.colour.colourspace.encoding_cctf(colours) if equalize_sd_amplitude: values = np.ones(values.shape) margin = 0 if equalize_sd_amplitude else 0.05 x_min, x_max = min(wavelengths), max(wavelengths) y_min, y_max = 0, max(values) + max(values) * margin polygon = Polygon( np.vstack([ (x_min, 0), tstack([wavelengths, values]), (x_max, 0), ]), facecolor='none', edgecolor='none') axes.add_patch(polygon) padding = 0.1 axes.bar( x=wavelengths - padding, height=max(values), width=1 + padding, color=colours, align='edge', clip_path=polygon) axes.plot(wavelengths, values, color=COLOUR_STYLE_CONSTANTS.colour.dark) settings = { 'axes': axes, 'bounding_box': (x_min, x_max, y_min, y_max), 'title': '{0} - {1}'.format(sd.strict_name, cmfs.strict_name), 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': 'Spectral Distribution', } settings.update(kwargs) return render(**settings)
def single_spd_plot(spd, cmfs='CIE 1931 2 Degree Standard Observer', out_of_gamut_clipping=True, **kwargs): """ Plots given spectral power distribution. Parameters ---------- spd : SpectralPowerDistribution Spectral power distribution to plot. out_of_gamut_clipping : bool, optional Out of gamut colours will be clipped if *True* otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. [1]_ cmfs : unicode Standard observer colour matching functions used for spectrum creation. \**kwargs : dict, optional Keywords arguments. Returns ------- Figure Current figure or None. Examples -------- >>> from colour import SpectralPowerDistribution >>> data = {400: 0.0641, 420: 0.0645, 440: 0.0562} >>> spd = SpectralPowerDistribution('Custom', data) >>> single_spd_plot(spd) # doctest: +SKIP """ cmfs = get_cmfs(cmfs) shape = cmfs.shape spd = spd.clone().interpolate(shape, 'Linear') wavelengths = spd.wavelengths values = spd.values y1 = values colours = XYZ_to_sRGB( wavelength_to_XYZ(wavelengths, cmfs), ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E'], apply_encoding_cctf=False) if not out_of_gamut_clipping: colours += np.abs(np.min(colours)) colours = DEFAULT_PLOTTING_ENCODING_CCTF(normalise_maximum(colours)) settings = { 'title': '{0} - {1}'.format(spd.title, cmfs.title), 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True } settings.update(kwargs) return colour_parameters_plot([ ColourParameter(x=x[0], y1=x[1], RGB=x[2]) for x in tuple(zip(wavelengths, y1, colours)) ], **settings)
def plot_with_rainbow_fill(ax=None, wavelength=None, flux=None, cmfs="CIE 1931 2 Degree Standard Observer", rainbowtop=np.inf, **kwargs): """ (This is still *real* blarg-y*.) Plot a spectrum, with a rainbow underneath. Parameters ---------- ax : matplotlib.axes._subplots.AxesSubplot The ax into which the rainbow should be drawn. cmfs : string The color matching function(s?) to use. Returns ------- ax : matplotlib.axes._subplots.AxesSubplot The ax into which the rainbow was drawn. """ if ax is None: ax = plt.gca() if wavelength is None: # create a grid of wavelengths (at which CMFss are useful) wavelength = CMFs.wavelengths # create y values that will be plotted (these could be spectrum) if flux is None: flux = np.ones_like(wavelength) # pull out only the values that *can* be converted to colors ok = (wavelength >= np.min(CMFs.wavelengths)) & (wavelength <= np.max( CMFs.wavelengths)) w, f = wavelength[ok], flux[ok] # clip the top of the box? f = np.minimum(f, rainbowtop) XYZ = wavelength_to_XYZ(w) # create colors at theose wavelengths colours = XYZ_to_plotting_colourspace(XYZ) # normalize the colors to their maximum? # colours = CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding( # normalise_maximum(colours)) colours = np.maximum(0, colours / np.max(colours)) x_min, x_max = min(w), max(w) y_min, y_max = 0, max(f) + max(f) * 0.05 # create a polygon to define the top of the bars? polygon = Polygon( np.vstack([ (x_min, 0), tstack([w, f]), (x_max, 0), ]), facecolor="none", edgecolor="none", ) ax.add_patch(polygon) # draw bars, with the colors at each vertical stripe padding = 0.0 dw = np.mean(np.diff(w)) ax.bar( x=w, height=f, width=dw, color=colours, clip_path=polygon, align="edge", clip_on=True, ) return ax
def plot_as_slit_rainbow(ax=None, wavelength=None, flux=None, cmfs="CIE 1931 2 Degree Standard Observer", **kwargs): """ (This is still *real* blarg-y*.) Plot a spectrum as a light source would be seen through a slit spectrometer, with vertical bands of light that are brighter or fainter depending on the intensity of the spectrum at that particular wavelength. Parameters ---------- ax : matplotlib.axes._subplots.AxesSubplot The ax into which the rainbow should be drawn. wavelength : array The wavelengths to include in the spectrum. In units of nm, but not as astropy units. flux : array The fluxes to include in the spectrum. In units of whatever, but not as astropy units. cmfs : string The color matching function(s?) to use. vector : bool Returns ------- ax : matplotlib.axes._subplots.AxesSubplot The ax into which the rainbow was drawn. """ # make sure our plotting ax is defined if ax is None: ax = plt.gca() # make sure we have a grid of wavelengths defined if wavelength is None: # create a grid of wavelengths (at which CMFss are useful) wavelength = CMFs.wavelengths # create y values that will be plotted (these could be spectrum) if flux is None: flux = np.ones_like(wavelength) # pull out only the values that *can* be converted to colors ok = (wavelength >= np.min(CMFs.wavelengths)) & (wavelength <= np.max( CMFs.wavelengths)) w, f = wavelength[ok], flux[ok] # get the XYZ for the wavelengths XYZ = wavelength_to_XYZ(w) # create colors at those wavelengths colours = XYZ_to_plotting_colourspace(XYZ) # normalize the colors to their maximum? # colours = CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding( # normalise_maximum(colours)) # normalize the brightness by the flux colours *= f[:, np.newaxis] # normalize to the brightest line colours = np.maximum(0, colours / np.max(colours)) # draw as an RGB color image with imshow ax.imshow( colours[np.newaxis, :, :], aspect="auto", extent=[np.min(w), np.max(w), 0, 1], interpolation="nearest", ) return ax
def plot_simple_rainbow(ax=None, wavelength=None, flux=None, cmfs="CIE 1931 2 Degree Standard Observer", **kwargs): """ Plot a simple horizontal rainbow, based off colour's plot_single_sd. Parameters ---------- ax : matplotlib.axes._subplots.AxesSubplot The ax into which the rainbow should be drawn. cmfs : string The color matching function(s?) to use. Returns ------- ax : matplotlib.axes._subplots.AxesSubplot The ax into which the rainbow was drawn. """ if ax is None: ax = plt.gca() # pull out the CMFss cmfs = first_item(filter_cmfs(cmfs).values()) if wavelength is None: # create a grid of wavelengths (at which CMFss are useful) wavelength = cmfs.wavelengths ok = (wavelength >= np.min(cmfs.wavelengths)) & (wavelength <= np.max( cmfs.wavelengths)) # create colors at theose wavelengths colours = XYZ_to_plotting_colourspace( wavelength_to_XYZ(wavelength[ok], cmfs), CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["E"], ) # normalize the colors to their maximum? colours = CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding( normalise_maximum(colours)) # create y values that will be plotted (these could be spectrum) if flux is None: flux = np.ones_like(wavelength) x_min, x_max = min(wavelength), max(wavelength) y_min, y_max = 0, max(flux) + max(flux) * 0.05 # create a polygon to define the top of the bars? polygon = Polygon( np.vstack([ (x_min, 0), tstack([wavelength[ok], flux[ok]]), (x_max, 0), ]), facecolor="none", edgecolor="none", ) ax.add_patch(polygon) # draw bars, with the colors at each vertical stripe padding = 0.2 ax.bar( x=wavelength[ok] - padding / 2, height=max(flux[ok]), width=1 + padding, color=colours, align="edge", ) return ax
def single_spd_plot(spd, cmfs='CIE 1931 2 Degree Standard Observer', out_of_gamut_clipping=True, **kwargs): """ Plots given spectral power distribution. Parameters ---------- spd : SpectralPowerDistribution Spectral power distribution to plot. out_of_gamut_clipping : bool, optional Out of gamut colours will be clipped if *True* otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. [1]_ cmfs : unicode Standard observer colour matching functions used for spectrum creation. \**kwargs : dict, optional Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import SpectralPowerDistribution >>> data = {400: 0.0641, 420: 0.0645, 440: 0.0562} >>> spd = SpectralPowerDistribution('Custom', data) >>> single_spd_plot(spd) # doctest: +SKIP True """ cmfs = get_cmfs(cmfs) shape = cmfs.shape spd = spd.clone().interpolate(shape, 'Linear') wavelengths = spd.wavelengths values = spd.values y1 = values colours = XYZ_to_sRGB( wavelength_to_XYZ(wavelengths, cmfs), ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E'], apply_OECF=False) if not out_of_gamut_clipping: colours += np.abs(np.min(colours)) colours = DEFAULT_PLOTTING_OECF(normalise(colours)) settings = { 'title': '{0} - {1}'.format(spd.title, cmfs.title), 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True} settings.update(kwargs) return colour_parameters_plot( [ColourParameter(x=x[0], y1=x[1], RGB=x[2]) for x in tuple(zip(wavelengths, y1, colours))], **settings)
def single_spd_plot(spd, cmfs='CIE 1931 2 Degree Standard Observer', out_of_gamut_clipping=True, **kwargs): """ Plots given spectral power distribution. Parameters ---------- spd : SpectralPowerDistribution Spectral power distribution to plot. out_of_gamut_clipping : bool, optional Whether to clip out of gamut colours otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. cmfs : unicode Standard observer colour matching functions used for spectrum creation. Other Parameters ---------------- \**kwargs : dict, optional {:func:`colour.plotting.render`}, Please refer to the documentation of the previously listed definition. Returns ------- Figure Current figure or None. References ---------- - :cite:`Spiker2015a` Examples -------- >>> from colour import SpectralPowerDistribution >>> data = { ... 500: 0.0651, ... 520: 0.0705, ... 540: 0.0772, ... 560: 0.0870, ... 580: 0.1128, ... 600: 0.1360 ... } >>> spd = SpectralPowerDistribution(data, name='Custom') >>> single_spd_plot(spd) # doctest: +SKIP """ axes = canvas(**kwargs).gca() cmfs = get_cmfs(cmfs) spd = spd.copy() spd.interpolator = LinearInterpolator wavelengths = cmfs.wavelengths[np.logical_and( cmfs.wavelengths >= max(min(cmfs.wavelengths), min(spd.wavelengths)), cmfs.wavelengths <= min(max(cmfs.wavelengths), max(spd.wavelengths)), )] values = spd[wavelengths] colours = XYZ_to_sRGB( wavelength_to_XYZ(wavelengths, cmfs), ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E'], apply_encoding_cctf=False) if not out_of_gamut_clipping: colours += np.abs(np.min(colours)) colours = DEFAULT_PLOTTING_ENCODING_CCTF(normalise_maximum(colours)) x_min, x_max = min(wavelengths), max(wavelengths) y_min, y_max = 0, max(values) polygon = Polygon(np.vstack([ (x_min, 0), tstack((wavelengths, values)), (x_max, 0), ]), facecolor='none', edgecolor='none') axes.add_patch(polygon) axes.bar(x=wavelengths, height=max(values), width=1, color=colours, align='edge', clip_path=polygon) axes.plot(wavelengths, values, color='black', linewidth=1) settings = { 'title': '{0} - {1}'.format(spd.strict_name, cmfs.strict_name), 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': 'Spectral Power Distribution', 'limits': (x_min, x_max, y_min, y_max), 'x_tighten': True, 'y_tighten': True } settings.update(kwargs) return render(**settings)
def plot_single_sd(sd, cmfs='CIE 1931 2 Degree Standard Observer', out_of_gamut_clipping=True, modulate_colours_with_sd_amplitude=False, equalize_sd_amplitude=False, **kwargs): """ Plots given spectral distribution. Parameters ---------- sd : SpectralDistribution Spectral distribution to plot. out_of_gamut_clipping : bool, optional Whether to clip out of gamut colours otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. modulate_colours_with_sd_amplitude : bool, optional Whether to modulate the colours with the spectral distribution amplitude. equalize_sd_amplitude : bool, optional Whether to equalize the spectral distribution amplitude. Equalization occurs after the colours modulation thus setting both arguments to *True* will generate a spectrum strip where each wavelength colour is modulated by the spectral distribution amplitude. The usual 5% margin above the spectral distribution is also omitted. cmfs : unicode Standard observer colour matching functions used for spectrum creation. Other Parameters ---------------- \\**kwargs : dict, optional {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, Please refer to the documentation of the previously listed definitions. Returns ------- tuple Current figure and axes. References ---------- :cite:`Spiker2015a` Examples -------- >>> from colour import SpectralDistribution >>> data = { ... 500: 0.0651, ... 520: 0.0705, ... 540: 0.0772, ... 560: 0.0870, ... 580: 0.1128, ... 600: 0.1360 ... } >>> sd = SpectralDistribution(data, name='Custom') >>> plot_single_sd(sd) # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, \ <matplotlib.axes._subplots.AxesSubplot object at 0x...>) .. image:: ../_static/Plotting_Plot_Single_SD.png :align: center :alt: plot_single_sd """ _figure, axes = artist(**kwargs) cmfs = first_item(filter_cmfs(cmfs).values()) sd = sd.copy() sd.interpolator = LinearInterpolator wavelengths = cmfs.wavelengths[np.logical_and( cmfs.wavelengths >= max(min(cmfs.wavelengths), min(sd.wavelengths)), cmfs.wavelengths <= min(max(cmfs.wavelengths), max(sd.wavelengths)), )] values = sd[wavelengths] colours = XYZ_to_plotting_colourspace( wavelength_to_XYZ(wavelengths, cmfs), ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['E'], apply_cctf_encoding=False) if not out_of_gamut_clipping: colours += np.abs(np.min(colours)) colours = normalise_maximum(colours) if modulate_colours_with_sd_amplitude: colours *= (values / np.max(values))[..., np.newaxis] colours = COLOUR_STYLE_CONSTANTS.colour.colourspace.cctf_encoding(colours) if equalize_sd_amplitude: values = np.ones(values.shape) margin = 0 if equalize_sd_amplitude else 0.05 x_min, x_max = min(wavelengths), max(wavelengths) y_min, y_max = 0, max(values) + max(values) * margin polygon = Polygon(np.vstack([ (x_min, 0), tstack([wavelengths, values]), (x_max, 0), ]), facecolor='none', edgecolor='none') axes.add_patch(polygon) padding = 0.1 axes.bar(x=wavelengths - padding, height=max(values), width=1 + padding, color=colours, align='edge', clip_path=polygon) axes.plot(wavelengths, values, color=COLOUR_STYLE_CONSTANTS.colour.dark) settings = { 'axes': axes, 'bounding_box': (x_min, x_max, y_min, y_max), 'title': '{0} - {1}'.format(sd.strict_name, cmfs.strict_name), 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': 'Spectral Distribution', } settings.update(kwargs) return render(**settings)
def plot_single_sd( sd: SpectralDistribution, cmfs: Union[MultiSpectralDistributions, str, Sequence[Union[ MultiSpectralDistributions, str]], ] = "CIE 1931 2 Degree Standard Observer", out_of_gamut_clipping: Boolean = True, modulate_colours_with_sd_amplitude: Boolean = False, equalize_sd_amplitude: Boolean = False, **kwargs: Any, ) -> Tuple[plt.Figure, plt.Axes]: """ Plot given spectral distribution. Parameters ---------- sd Spectral distribution to plot. cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. out_of_gamut_clipping Whether to clip out of gamut colours otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. modulate_colours_with_sd_amplitude Whether to modulate the colours with the spectral distribution amplitude. equalize_sd_amplitude Whether to equalize the spectral distribution amplitude. Equalization occurs after the colours modulation thus setting both arguments to *True* will generate a spectrum strip where each wavelength colour is modulated by the spectral distribution amplitude. The usual 5% margin above the spectral distribution is also omitted. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. References ---------- :cite:`Spiker2015a` Examples -------- >>> from colour import SpectralDistribution >>> data = { ... 500: 0.0651, ... 520: 0.0705, ... 540: 0.0772, ... 560: 0.0870, ... 580: 0.1128, ... 600: 0.1360 ... } >>> sd = SpectralDistribution(data, name='Custom') >>> plot_single_sd(sd) # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...AxesSubplot...>) .. image:: ../_static/Plotting_Plot_Single_SD.png :align: center :alt: plot_single_sd """ _figure, axes = artist(**kwargs) cmfs = cast(MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values())) sd = cast(SpectralDistribution, sd.copy()) sd.interpolator = LinearInterpolator wavelengths = cmfs.wavelengths[np.logical_and( cmfs.wavelengths >= max(min(cmfs.wavelengths), min(sd.wavelengths)), cmfs.wavelengths <= min(max(cmfs.wavelengths), max(sd.wavelengths)), )] values = as_float_array(sd[wavelengths]) RGB = XYZ_to_plotting_colourspace( wavelength_to_XYZ(wavelengths, cmfs), CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["E"], apply_cctf_encoding=False, ) if not out_of_gamut_clipping: RGB += np.abs(np.min(RGB)) RGB = normalise_maximum(RGB) if modulate_colours_with_sd_amplitude: RGB *= (values / np.max(values))[..., np.newaxis] RGB = CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding(RGB) if equalize_sd_amplitude: values = ones(values.shape) margin = 0 if equalize_sd_amplitude else 0.05 x_min, x_max = min(wavelengths), max(wavelengths) y_min, y_max = 0, max(values) + max(values) * margin polygon = Polygon( np.vstack([ (x_min, 0), tstack([wavelengths, values]), (x_max, 0), ]), facecolor="none", edgecolor="none", zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, ) axes.add_patch(polygon) padding = 0.1 axes.bar( x=wavelengths - padding, height=max(values), width=1 + padding, color=RGB, align="edge", clip_path=polygon, zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, ) axes.plot( wavelengths, values, color=CONSTANTS_COLOUR_STYLE.colour.dark, zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, ) settings: Dict[str, Any] = { "axes": axes, "bounding_box": (x_min, x_max, y_min, y_max), "title": f"{sd.strict_name} - {cmfs.strict_name}", "x_label": "Wavelength $\\lambda$ (nm)", "y_label": "Spectral Distribution", } settings.update(kwargs) return render(**settings)