def vs_colorimetry_data(spd_test, spd_reference, spds_vs, cmfs, chromatic_adaptation=False): XYZ_t = spectral_to_XYZ(spd_test, cmfs) XYZ_t /= XYZ_t[1] XYZ_r = spectral_to_XYZ(spd_reference, cmfs) XYZ_r /= XYZ_r[1] xy_r = XYZ_to_xy(XYZ_r) vs_data = [] for _key, value in sorted(VS_INDEXES_TO_NAMES.items()): spd_vs = spds_vs.get(value) XYZ_vs = spectral_to_XYZ(spd_vs, cmfs, spd_test) XYZ_vs /= 100 if chromatic_adaptation: XYZ_vs = chromatic_adaptation_VonKries(XYZ_vs, XYZ_t, XYZ_r, transform='CMCCAT2000') Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r) _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs) vs_data.append(VS_ColorimetryData(spd_vs.name, XYZ_vs, Lab_vs, C_vs)) return vs_data
def test_spectral_to_XYZ(self): """ Tests :func:`colour.colorimetry.tristimulus.spectral_to_XYZ` definition. """ cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer') np.testing.assert_almost_equal( spectral_to_XYZ( RELATIVE_SPD_DATA.zeros(cmfs.shape), cmfs, ILLUMINANTS_RELATIVE_SPDS.get('A').clone().zeros(cmfs.shape)), np.array([14.46371626, 10.85832347, 2.04664796]), decimal=7) cmfs = CMFS.get('CIE 1964 10 Degree Standard Observer') np.testing.assert_almost_equal( spectral_to_XYZ( RELATIVE_SPD_DATA.zeros(cmfs.shape), cmfs, ILLUMINANTS_RELATIVE_SPDS.get('C').clone().zeros(cmfs.shape)), np.array([10.7704252, 9.44870313, 6.62742289]), decimal=7) np.testing.assert_almost_equal( spectral_to_XYZ( RELATIVE_SPD_DATA.zeros(cmfs.shape), cmfs, ILLUMINANTS_RELATIVE_SPDS.get('F2').clone().zeros(cmfs.shape)), np.array([11.57830745, 9.98744967, 3.95396539]), decimal=7)
def vs_colorimetry_data(spd_test, spd_reference, spds_vs, cmfs, chromatic_adaptation=False): """ Returns the *VS test colour samples* colorimetry data. Parameters ---------- spd_test : SpectralPowerDistribution Test spectral power distribution. spd_reference : SpectralPowerDistribution Reference spectral power distribution. spds_vs : dict *VS test colour samples* spectral power distributions. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. chromatic_adaptation : bool, optional Perform chromatic adaptation. Returns ------- list *VS test colour samples* colorimetry data. """ XYZ_t = spectral_to_XYZ(spd_test, cmfs) XYZ_t /= XYZ_t[1] XYZ_r = spectral_to_XYZ(spd_reference, cmfs) XYZ_r /= XYZ_r[1] xy_r = XYZ_to_xy(XYZ_r) vs_data = [] for _key, value in sorted(VS_INDEXES_TO_NAMES.items()): spd_vs = spds_vs.get(value) XYZ_vs = spectral_to_XYZ(spd_vs, cmfs, spd_test) XYZ_vs /= 100 if chromatic_adaptation: XYZ_vs = chromatic_adaptation_VonKries(XYZ_vs, XYZ_t, XYZ_r, transform='CMCCAT2000') Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r) _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs) vs_data.append( VS_ColorimetryData(spd_vs.name, XYZ_vs, Lab_vs, C_vs)) return vs_data
def vs_colorimetry_data(spd_test, spd_reference, spds_vs, cmfs, chromatic_adaptation=False): """ Returns the *VS test colour samples* colorimetry data. Parameters ---------- spd_test : SpectralPowerDistribution Test spectral power distribution. spd_reference : SpectralPowerDistribution Reference spectral power distribution. spds_vs : dict *VS test colour samples* spectral power distributions. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. chromatic_adaptation : bool, optional Perform chromatic adaptation. Returns ------- list *VS test colour samples* colorimetry data. """ XYZ_t = spectral_to_XYZ(spd_test, cmfs) XYZ_t /= XYZ_t[1] XYZ_r = spectral_to_XYZ(spd_reference, cmfs) XYZ_r /= XYZ_r[1] xy_r = XYZ_to_xy(XYZ_r) vs_data = [] for _key, value in sorted(VS_INDEXES_TO_NAMES.items()): spd_vs = spds_vs[value] XYZ_vs = spectral_to_XYZ(spd_vs, cmfs, spd_test) XYZ_vs /= 100 if chromatic_adaptation: XYZ_vs = chromatic_adaptation_VonKries(XYZ_vs, XYZ_t, XYZ_r, transform='CMCCAT2000') Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r) _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs) vs_data.append(VS_ColorimetryData(spd_vs.name, XYZ_vs, Lab_vs, C_vs)) return vs_data
def planckian_table(uv, cmfs, start, end, count): """ Returns a planckian table from given *CIE UCS* colourspace *uv* chromaticity coordinates, colour matching functions and temperature range using Ohno (2013) method. Parameters ---------- uv : array_like *uv* chromaticity coordinates. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. start : numeric Temperature range start in kelvins. end : numeric Temperature range end in kelvins. count : int Temperatures count in the planckian table. Returns ------- list Planckian table. Examples -------- >>> from colour import STANDARD_OBSERVERS_CMFS >>> from pprint import pprint >>> cmfs = 'CIE 1931 2 Degree Standard Observer' >>> cmfs = STANDARD_OBSERVERS_CMFS.get(cmfs) >>> uv = np.array([0.1978, 0.3122]) >>> pprint(planckian_table(uv, cmfs, 1000, 1010, 10)) # noqa # doctest: +ELLIPSIS [PlanckianTable_Tuvdi(Ti=1000.0, ui=0.4480108..., vi=0.3546249..., di=0.2537821...), PlanckianTable_Tuvdi(Ti=1001.1111111..., ui=0.4477508..., vi=0.3546475..., di=0.2535294...), PlanckianTable_Tuvdi(Ti=1002.2222222..., ui=0.4474910..., vi=0.3546700..., di=0.2532771...), PlanckianTable_Tuvdi(Ti=1003.3333333..., ui=0.4472316..., vi=0.3546924..., di=0.2530251...), PlanckianTable_Tuvdi(Ti=1004.4444444..., ui=0.4469724..., vi=0.3547148..., di=0.2527734...), PlanckianTable_Tuvdi(Ti=1005.5555555..., ui=0.4467136..., vi=0.3547372..., di=0.2525220...), PlanckianTable_Tuvdi(Ti=1006.6666666..., ui=0.4464550..., vi=0.3547595..., di=0.2522710...), PlanckianTable_Tuvdi(Ti=1007.7777777..., ui=0.4461968..., vi=0.3547817..., di=0.2520202...), PlanckianTable_Tuvdi(Ti=1008.8888888..., ui=0.4459389..., vi=0.3548040..., di=0.2517697...), PlanckianTable_Tuvdi(Ti=1010.0, ui=0.4456812..., vi=0.3548261..., di=0.2515196...)] """ ux, vx = uv shape = cmfs.shape table = [] for Ti in np.linspace(start, end, count): spd = blackbody_spd(Ti, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) ui, vi = UCS_to_uv(UVW) di = np.sqrt((ux - ui) ** 2 + (vx - vi) ** 2) table.append(PLANCKIAN_TABLE_TUVD(Ti, ui, vi, di)) return table
def planckian_table(uv, cmfs, start, end, count): """ Returns a planckian table from given *CIE UCS* colourspace *uv* chromaticity coordinates, colour matching functions and temperature range using *Yoshi Ohno (2013)* method. Parameters ---------- uv : array_like *uv* chromaticity coordinates. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. start : numeric Temperature range start in kelvins. end : numeric Temperature range end in kelvins. count : int Temperatures count in the planckian table. Returns ------- list Planckian table. Examples -------- >>> from colour import STANDARD_OBSERVERS_CMFS >>> from pprint import pprint >>> cmfs = 'CIE 1931 2 Degree Standard Observer' >>> cmfs = STANDARD_OBSERVERS_CMFS.get(cmfs) >>> pprint(planckian_table((0.1978, 0.3122), cmfs, 1000, 1010, 10)) # noqa # doctest: +ELLIPSIS [PlanckianTable_Tuvdi(Ti=1000.0, ui=0.4480108..., vi=0.3546249..., di=0.2537821...), PlanckianTable_Tuvdi(Ti=1001.1111111..., ui=0.4477508..., vi=0.3546475..., di=0.2535294...), PlanckianTable_Tuvdi(Ti=1002.2222222..., ui=0.4474910..., vi=0.3546700..., di=0.2532771...), PlanckianTable_Tuvdi(Ti=1003.3333333..., ui=0.4472316..., vi=0.3546924..., di=0.2530251...), PlanckianTable_Tuvdi(Ti=1004.4444444..., ui=0.4469724..., vi=0.3547148..., di=0.2527734...), PlanckianTable_Tuvdi(Ti=1005.5555555..., ui=0.4467136..., vi=0.3547372..., di=0.2525220...), PlanckianTable_Tuvdi(Ti=1006.6666666..., ui=0.4464550..., vi=0.3547595..., di=0.2522710...), PlanckianTable_Tuvdi(Ti=1007.7777777..., ui=0.4461968..., vi=0.3547817..., di=0.2520202...), PlanckianTable_Tuvdi(Ti=1008.8888888..., ui=0.4459389..., vi=0.3548040..., di=0.2517697...), PlanckianTable_Tuvdi(Ti=1010.0, ui=0.4456812..., vi=0.3548261..., di=0.2515196...)] """ ux, vx = uv shape = cmfs.shape table = [] for Ti in np.linspace(start, end, count): spd = blackbody_spd(Ti, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) ui, vi = UCS_to_uv(UVW) di = math.sqrt((ux - ui)**2 + (vx - vi)**2) table.append(PLANCKIAN_TABLE_TUVD(Ti, ui, vi, di)) return table
def spds_CIE_1976_UCS_chromaticity_diagram_plot( spds, cmfs='CIE 1931 2 Degree Standard Observer', annotate=True, **kwargs): """ Plots given spectral power distribution chromaticity coordinates into the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- spds : list, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. annotate : bool Should resulting chromaticity coordinates annotated with their respective spectral power distribution names. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> A = ILLUMINANTS_RELATIVE_SPDS['A'] >>> D65 = ILLUMINANTS_RELATIVE_SPDS['D65'] >>> spds_CIE_1976_UCS_chromaticity_diagram_plot([A, D65]) # doctest: +SKIP True """ CIE_1976_UCS_chromaticity_diagram_plot(standalone=False, **kwargs) cmfs = get_cmfs(cmfs) cmfs_shape = cmfs.shape for spd in spds: spd = spd.clone().align(cmfs_shape) XYZ = spectral_to_XYZ(spd) / 100 uv = Luv_to_uv(XYZ_to_Luv(XYZ)) pylab.plot(uv[0], uv[1], 'o', color='white') if spd.name is not None and annotate: pylab.annotate(spd.name, xy=uv, xytext=(50, 30), textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=0.2')) display(standalone=True)
def colour_quality_scale(spd_test, T, additional_data=False): cmfs = STANDARD_OBSERVERS_CMFS.get('CIE 1931 2 Degree Standard Observer') shape = cmfs.shape CCT, _D_uv = T if CCT < 5000: spd_reference = blackbody_spd(CCT, shape) else: xy = CCT_to_xy_CIE_D(CCT) spd_reference = D_illuminant_relative_spd(xy) spd_reference.align(shape) test_vs_colorimetry_data = vs_colorimetry_data(spd_test, spd_reference, VS_SPDS, cmfs, chromatic_adaptation=True) reference_vs_colorimetry_data = vs_colorimetry_data( spd_reference, spd_reference, VS_SPDS, cmfs) XYZ_r = spectral_to_XYZ(spd_reference, cmfs) XYZ_r /= XYZ_r[1] CCT_f = CCT_factor(reference_vs_colorimetry_data, XYZ_r) Q_as = colour_quality_scales(test_vs_colorimetry_data, reference_vs_colorimetry_data, CCT_f) D_E_RMS = delta_E_RMS(Q_as, 'D_E_ab') D_Ep_RMS = delta_E_RMS(Q_as, 'D_Ep_ab') Q_a = scale_conversion(D_Ep_RMS, CCT_f) Q_f = scale_conversion(D_E_RMS, CCT_f, 2.928) p_delta_C = np.average([ sample_data.D_C_ab if sample_data.D_C_ab > 0 else 0 for sample_data in Q_as.values() ]) Q_p = 100 - 3.6 * (D_Ep_RMS - p_delta_C) G_t = gamut_area( [vs_CQS_data.Lab for vs_CQS_data in test_vs_colorimetry_data]) G_r = gamut_area( [vs_CQS_data.Lab for vs_CQS_data in reference_vs_colorimetry_data]) Q_g = G_t / D65_GAMUT_AREA * 100 Q_d = G_t / G_r * CCT_f * 100 if additional_data: return CQS_Specification( spd_test.name, Q_a, Q_f, Q_p, Q_g, Q_d, Q_as, (test_vs_colorimetry_data, reference_vs_colorimetry_data)) else: return Q_a
def blackbody_colours_plot(shape=SpectralShape(150, 12500, 50), cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots blackbody colours. Parameters ---------- shape : SpectralShape, optional Spectral shape to use as plot boundaries. cmfs : unicode, optional Standard observer colour matching functions. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> blackbody_colours_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs colours = [] temperatures = [] for temperature in shape: spd = blackbody_spd(temperature, cmfs.shape) XYZ = spectral_to_XYZ(spd, cmfs) RGB = normalise(XYZ_to_sRGB(XYZ / 100)) colours.append(RGB) temperatures.append(temperature) settings = { 'title': 'Blackbody Colours', 'x_label': 'Temperature K', 'y_label': '', 'x_tighten': True, 'x_ticker': True, 'y_ticker': False } settings.update(kwargs) return colour_parameters_plot([ colour_parameter(x=x[0], RGB=x[1]) for x in tuple(zip(temperatures, colours)) ], **settings)
def blackbody_colours_plot(shape=SpectralShape(150, 12500, 50), cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots blackbody colours. Parameters ---------- shape : SpectralShape, optional Spectral shape to use as plot boundaries. cmfs : unicode, optional Standard observer colour matching functions. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> blackbody_colours_plot() # doctest: +SKIP True """ cmfs = get_cmfs(cmfs) colours = [] temperatures = [] for temperature in shape: spd = blackbody_spd(temperature, cmfs.shape) XYZ = spectral_to_XYZ(spd, cmfs) RGB = normalise(XYZ_to_sRGB(XYZ / 100)) colours.append(RGB) temperatures.append(temperature) settings = { 'title': 'Blackbody Colours', 'x_label': 'Temperature K', 'y_label': '', 'x_tighten': True, 'x_ticker': True, 'y_ticker': False} settings.update(kwargs) return colour_parameters_plot([colour_parameter(x=x[0], RGB=x[1]) for x in tuple(zip(temperatures, colours))], **settings)
def blackbody_spectral_radiance_plot( temperature=3500, cmfs='CIE 1931 2 Degree Standard Observer', blackbody='VY Canis Major', **kwargs): """ Plots given blackbody spectral radiance. Parameters ---------- temperature : numeric, optional Blackbody temperature. cmfs : unicode, optional Standard observer colour matching functions. blackbody : unicode, optional Blackbody name. \**kwargs : dict, optional Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> blackbody_spectral_radiance_plot() # doctest: +SKIP True """ canvas(**kwargs) cmfs = get_cmfs(cmfs) matplotlib.pyplot.subplots_adjust(hspace=0.4) spd = blackbody_spd(temperature, cmfs.shape) matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(211) settings = { 'title': '{0} - Spectral Radiance'.format(blackbody), 'y_label': 'W / (sr m$^2$) / m', 'standalone': False} settings.update(kwargs) single_spd_plot(spd, cmfs.name, **settings) XYZ = spectral_to_XYZ(spd, cmfs) RGB = normalise(XYZ_to_sRGB(XYZ / 100)) matplotlib.pyplot.subplot(212) settings = {'title': '{0} - Colour'.format(blackbody), 'x_label': '{0}K'.format(temperature), 'y_label': '', 'aspect': None, 'standalone': False} single_colour_plot(ColourParameter(name='', RGB=RGB), **settings) settings = { 'standalone': True} settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def spds_CIE_1976_UCS_chromaticity_diagram_plot( spds, cmfs='CIE 1931 2 Degree Standard Observer', annotate=True, **kwargs): """ Plots given spectral power distribution chromaticity coordinates into the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- spds : array_like, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. annotate : bool Should resulting chromaticity coordinates annotated with their respective spectral power distribution names. \**kwargs : dict, optional Keywords arguments. Returns ------- Figure Current figure or None. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> A = ILLUMINANTS_RELATIVE_SPDS['A'] >>> D65 = ILLUMINANTS_RELATIVE_SPDS['D65'] >>> spds_CIE_1976_UCS_chromaticity_diagram_plot([A, D65]) # doctest: +SKIP """ settings = {} settings.update(kwargs) settings.update({'standalone': False}) CIE_1976_UCS_chromaticity_diagram_plot(cmfs=cmfs, **settings) for spd in spds: XYZ = spectral_to_XYZ(spd) / 100 uv = Luv_to_uv(XYZ_to_Luv(XYZ)) pylab.plot(uv[0], uv[1], 'o', color='white') if spd.name is not None and annotate: pylab.annotate(spd.name, xy=uv, xytext=(50, 30), color='black', textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=0.2')) settings.update({ 'x_tighten': True, 'y_tighten': True, 'limits': (-0.1, 0.7, -0.1, 0.7), 'standalone': True}) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def multi_spd_plot(spds, cmfs='CIE 1931 2 Degree Standard Observer', use_spds_colours=False, normalise_spds_colours=False, **kwargs): """ Plots given spectral power distributions. Parameters ---------- spds : list Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for spectrum creation. use_spds_colours : bool, optional Use spectral power distributions colours. normalise_spds_colours : bool Should spectral power distributions colours normalised. \**kwargs : dict, optional Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import SpectralPowerDistribution >>> data1 = {400: 0.0641, 420: 0.0645, 440: 0.0562} >>> data2 = {400: 0.134, 420: 0.789, 440: 1.289} >>> spd1 = SpectralPowerDistribution('Custom1', data1) >>> spd2 = SpectralPowerDistribution('Custom2', data2) >>> multi_spd_plot([spd1, spd2]) # doctest: +SKIP True """ canvas(**kwargs) cmfs = get_cmfs(cmfs) if use_spds_colours: illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D65') x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] for spd in spds: wavelengths, values = tuple(zip(*spd.items)) shape = spd.shape x_limit_min.append(shape.start) x_limit_max.append(shape.end) y_limit_min.append(min(values)) y_limit_max.append(max(values)) if use_spds_colours: XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100 if normalise_spds_colours: XYZ = normalise(XYZ, clip=False) RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1) pylab.plot(wavelengths, values, color=RGB, label=spd.title, linewidth=2) else: pylab.plot(wavelengths, values, label=spd.title, linewidth=2) settings = { 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True, 'legend': True, 'legend_location': 'upper left', 'limits': (min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max))} settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def the_blue_sky_plot(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the blue sky. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions. \**kwargs : dict, optional Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> the_blue_sky_plot() # doctest: +SKIP True """ canvas(**kwargs) cmfs, name = get_cmfs(cmfs), cmfs ASTM_G_173_spd = ASTM_G_173_ETR.clone() rayleigh_spd = rayleigh_scattering_spd() ASTM_G_173_spd.align(rayleigh_spd.shape) spd = rayleigh_spd * ASTM_G_173_spd matplotlib.pyplot.subplots_adjust(hspace=0.4) matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(211) settings = { 'title': 'The Blue Sky - Synthetic Spectral Power Distribution', 'y_label': u'W / m-2 / nm-1', 'standalone': False } settings.update(kwargs) single_spd_plot(spd, name, **settings) matplotlib.pyplot.subplot(212) settings = { 'title': 'The Blue Sky - Colour', 'x_label': ('The sky is blue because molecules in the atmosphere ' 'scatter shorter wavelengths more than longer ones.\n' 'The synthetic spectral power distribution is computed as ' 'follows: ' '(ASTM G-173 ETR * Standard Air Rayleigh Scattering).'), 'y_label': '', 'aspect': None, 'standalone': False } blue_sky_color = XYZ_to_sRGB(spectral_to_XYZ(spd)) single_colour_plot(ColourParameter('', normalise(blue_sky_color)), **settings) settings = {'standalone': True} settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def multi_spd_plot(spds, cmfs='CIE 1931 2 Degree Standard Observer', use_spds_colours=False, normalise_spds_colours=False, **kwargs): """ Plots given spectral power distributions. Parameters ---------- spds : list Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for spectrum creation. use_spds_colours : bool, optional Whether to use spectral power distributions colours. normalise_spds_colours : bool Whether to normalise spectral power distributions colours. 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. Examples -------- >>> from colour import SpectralPowerDistribution >>> data_1 = { ... 500: 0.004900, ... 510: 0.009300, ... 520: 0.063270, ... 530: 0.165500, ... 540: 0.290400, ... 550: 0.433450, ... 560: 0.594500 ... } >>> data_2 = { ... 500: 0.323000, ... 510: 0.503000, ... 520: 0.710000, ... 530: 0.862000, ... 540: 0.954000, ... 550: 0.994950, ... 560: 0.995000 ... } >>> spd1 = SpectralPowerDistribution(data_1, name='Custom 1') >>> spd2 = SpectralPowerDistribution(data_2, name='Custom 2') >>> multi_spd_plot([spd1, spd2]) # doctest: +SKIP """ canvas(**kwargs) cmfs = get_cmfs(cmfs) if use_spds_colours: illuminant = ILLUMINANTS_RELATIVE_SPDS['D65'] x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] for spd in spds: wavelengths, values = spd.wavelengths, spd.values shape = spd.shape x_limit_min.append(shape.start) x_limit_max.append(shape.end) y_limit_min.append(min(values)) y_limit_max.append(max(values)) if use_spds_colours: XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100 if normalise_spds_colours: XYZ = normalise_maximum(XYZ, clip=False) RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1) pylab.plot(wavelengths, values, color=RGB, label=spd.strict_name, linewidth=1) else: pylab.plot(wavelengths, values, label=spd.strict_name, linewidth=1) settings = { 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True, 'y_tighten': True, 'legend': True, 'legend_location': 'upper left', 'limits': (min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max)) } settings.update(kwargs) return render(**settings)
def _tcs_colorimetry_data(test_spd, reference_spd, tsc_spds, cmfs, chromatic_adaptation=False): """ Returns the *test colour samples* colorimetry data. Parameters ---------- test_spd : SpectralPowerDistribution Test spectral power distribution. reference_spd : SpectralPowerDistribution Reference spectral power distribution. tsc_spds : dict Test colour samples. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. chromatic_adaptation : bool, optional Perform chromatic adaptation. Returns ------- list *Test colour samples* colorimetry data. """ test_XYZ = spectral_to_XYZ(test_spd, cmfs) test_uv = np.ravel(UCS_to_uv(XYZ_to_UCS(test_XYZ))) test_u, test_v = test_uv[0], test_uv[1] reference_XYZ = spectral_to_XYZ(reference_spd, cmfs) reference_uv = np.ravel(UCS_to_uv(XYZ_to_UCS(reference_XYZ))) reference_u, reference_v = reference_uv[0], reference_uv[1] tcs_data = [] for key, value in sorted(TCS_INDEXES_TO_NAMES.items()): tcs_spd = tsc_spds.get(value) tcs_XYZ = spectral_to_XYZ(tcs_spd, cmfs, test_spd) tcs_xyY = np.ravel(XYZ_to_xyY(tcs_XYZ)) tcs_uv = np.ravel(UCS_to_uv(XYZ_to_UCS(tcs_XYZ))) tcs_u, tcs_v = tcs_uv[0], tcs_uv[1] if chromatic_adaptation: c = lambda x, y: (4 - x - 10 * y) / y d = lambda x, y: (1.708 * y + 0.404 - 1.481 * x) / y test_c, test_d = c(test_u, test_v), d(test_u, test_v) reference_c, reference_d = (c(reference_u, reference_v), d(reference_u, reference_v)) tcs_c, tcs_d = c(tcs_u, tcs_v), d(tcs_u, tcs_v) tcs_u = ((10.872 + 0.404 * reference_c / test_c * tcs_c - 4 * reference_d / test_d * tcs_d) / (16.518 + 1.481 * reference_c / test_c * tcs_c - reference_d / test_d * tcs_d)) tcs_v = (5.52 / (16.518 + 1.481 * reference_c / test_c * tcs_c - reference_d / test_d * tcs_d)) tcs_W = 25 * tcs_xyY[-1]**(1 / 3) - 17 tcs_U = 13 * tcs_W * (tcs_u - reference_u) tcs_V = 13 * tcs_W * (tcs_v - reference_v) tcs_data.append( TSC_COLORIMETRY_DATA_NXYZUVUVW(tcs_spd.name, tcs_XYZ, tcs_uv, np.array([tcs_U, tcs_V, tcs_W]))) return tcs_data
def tcs_colorimetry_data(spd_t, spd_r, spds_tcs, cmfs, chromatic_adaptation=False): """ Returns the *test colour samples* colorimetry data. Parameters ---------- spd_t : SpectralPowerDistribution Test spectral power distribution. spd_r : SpectralPowerDistribution Reference spectral power distribution. spds_tcs : dict *Test colour samples* spectral power distributions. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. chromatic_adaptation : bool, optional Perform chromatic adaptation. Returns ------- list *Test colour samples* colorimetry data. """ XYZ_t = spectral_to_XYZ(spd_t, cmfs) uv_t = np.ravel(UCS_to_uv(XYZ_to_UCS(XYZ_t))) u_t, v_t = uv_t[0], uv_t[1] XYZ_r = spectral_to_XYZ(spd_r, cmfs) uv_r = np.ravel(UCS_to_uv(XYZ_to_UCS(XYZ_r))) u_r, v_r = uv_r[0], uv_r[1] tcs_data = [] for _key, value in sorted(TCS_INDEXES_TO_NAMES.items()): spd_tcs = spds_tcs.get(value) XYZ_tcs = spectral_to_XYZ(spd_tcs, cmfs, spd_t) xyY_tcs = np.ravel(XYZ_to_xyY(XYZ_tcs)) uv_tcs = np.ravel(UCS_to_uv(XYZ_to_UCS(XYZ_tcs))) u_tcs, v_tcs = uv_tcs[0], uv_tcs[1] if chromatic_adaptation: c = lambda x, y: (4 - x - 10 * y) / y d = lambda x, y: (1.708 * y + 0.404 - 1.481 * x) / y c_t, d_t = c(u_t, v_t), d(u_t, v_t) c_r, d_r = (c(u_r, v_r), d(u_r, v_r)) tcs_c, tcs_d = c(u_tcs, v_tcs), d(u_tcs, v_tcs) u_tcs = ((10.872 + 0.404 * c_r / c_t * tcs_c - 4 * d_r / d_t * tcs_d) / (16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d)) v_tcs = (5.52 / (16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d)) W_tcs = 25 * xyY_tcs[-1] ** (1 / 3) - 17 U_tcs = 13 * W_tcs * (u_tcs - u_r) V_tcs = 13 * W_tcs * (v_tcs - v_r) tcs_data.append( TCS_ColorimetryData(spd_tcs.name, XYZ_tcs, uv_tcs, np.array([U_tcs, V_tcs, W_tcs]))) return tcs_data
def CCT_to_uv_Ohno2013( CCT, D_uv=0, cmfs=STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer']): """ Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}`, :math:`\Delta_{uv}` and colour matching functions using *Ohno (2013)* method. Parameters ---------- CCT : numeric Correlated colour temperature :math:`T_{cp}`. D_uv : numeric, optional :math:`\Delta_{uv}`. cmfs : XYZ_ColourMatchingFunctions, optional Standard observer colour matching functions. Returns ------- ndarray *CIE UCS* colourspace *uv* chromaticity coordinates. References ---------- - :cite:`Ohno2014a` Examples -------- >>> from colour import STANDARD_OBSERVERS_CMFS >>> cmfs = STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> CCT = 6507.4342201047066 >>> D_uv = 0.003223690901513 >>> CCT_to_uv_Ohno2013(CCT, D_uv, cmfs) # doctest: +ELLIPSIS array([ 0.1977999..., 0.3122004...]) """ cmfs = cmfs.copy().trim(ASTME30815_PRACTISE_SHAPE) shape = cmfs.shape delta = 0.01 spd = blackbody_spd(CCT, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) u0, v0 = UCS_to_uv(UVW) if D_uv == 0: return np.array([u0, v0]) else: spd = blackbody_spd(CCT + delta, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) u1, v1 = UCS_to_uv(UVW) du = u0 - u1 dv = v0 - v1 u = u0 - D_uv * (dv / np.hypot(du, dv)) v = v0 + D_uv * (du / np.hypot(du, dv)) return np.array([u, v])
def blackbody_colours_plot(shape=SpectralShape(150, 12500, 50), cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots blackbody colours. Parameters ---------- shape : SpectralShape, optional Spectral shape to use as plot boundaries. cmfs : unicode, optional Standard observer colour matching functions. 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. Examples -------- >>> blackbody_colours_plot() # doctest: +SKIP """ axes = canvas(**kwargs).gca() cmfs = get_cmfs(cmfs) colours = [] temperatures = [] with suppress_warnings(): for temperature in shape: spd = blackbody_spd(temperature, cmfs.shape) XYZ = spectral_to_XYZ(spd, cmfs) RGB = normalise_maximum(XYZ_to_sRGB(XYZ / 100)) colours.append(RGB) temperatures.append(temperature) x_min, x_max = min(temperatures), max(temperatures) y_min, y_max = 0, 1 axes.bar(x=temperatures, height=1, width=shape.interval, color=colours, align='edge') settings = { 'title': 'Blackbody Colours', 'x_label': 'Temperature K', 'y_label': None, 'limits': (x_min, x_max, y_min, y_max), 'x_tighten': True, 'y_tighten': True, 'y_ticker': False } settings.update(kwargs) return render(**settings)
def CCT_to_uv_Ohno2013( CCT, D_uv=0, cmfs=STANDARD_OBSERVERS_CMFS.get('CIE 1931 2 Degree Standard Observer')): """ Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}`, :math:`\Delta_{uv}` and colour matching functions using Ohno (2013) method. Parameters ---------- CCT : numeric Correlated colour temperature :math:`T_{cp}`. D_uv : numeric, optional :math:`\Delta_{uv}`. cmfs : XYZ_ColourMatchingFunctions, optional Standard observer colour matching functions. Returns ------- ndarray *CIE UCS* colourspace *uv* chromaticity coordinates. References ---------- .. [4] Ohno, Y. (2014). Practical Use and Calculation of CCT and Duv. LEUKOS, 10(1), 47–55. doi:10.1080/15502724.2014.839020 Examples -------- >>> from colour import STANDARD_OBSERVERS_CMFS >>> cmfs = 'CIE 1931 2 Degree Standard Observer' >>> cmfs = STANDARD_OBSERVERS_CMFS.get(cmfs) >>> CCT = 6507.4342201047066 >>> D_uv = 0.003223690901512735 >>> CCT_to_uv_Ohno2013(CCT, D_uv, cmfs) # doctest: +ELLIPSIS array([ 0.1978003..., 0.3122005...]) """ shape = cmfs.shape delta = 0.01 spd = blackbody_spd(CCT, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) u0, v0 = UCS_to_uv(UVW) if D_uv == 0: return np.array([u0, v0]) else: spd = blackbody_spd(CCT + delta, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) u1, v1 = UCS_to_uv(UVW) du = u0 - u1 dv = v0 - v1 u = u0 - D_uv * (dv / np.sqrt(du**2 + dv**2)) v = v0 + D_uv * (du / np.sqrt(du**2 + dv**2)) return np.array([u, v])
def the_blue_sky_plot( cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the blue sky. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> the_blue_sky_plot() # doctest: +SKIP True """ canvas(**kwargs) cmfs, name = get_cmfs(cmfs), cmfs ASTM_G_173_spd = ASTM_G_173_ETR.clone() rayleigh_spd = rayleigh_scattering_spd() ASTM_G_173_spd.align(rayleigh_spd.shape) spd = rayleigh_spd * ASTM_G_173_spd matplotlib.pyplot.subplots_adjust(hspace=0.4) matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(211) settings = { 'title': 'The Blue Sky - Synthetic Spectral Power Distribution', 'y_label': u'W / m-2 / nm-1', 'standalone': False} settings.update(kwargs) single_spd_plot(spd, name, **settings) matplotlib.pyplot.subplot(212) settings = { 'title': 'The Blue Sky - Colour', 'x_label': ('The sky is blue because molecules in the atmosphere ' 'scatter shorter wavelengths more than longer ones.\n' 'The synthetic spectral power distribution is computed as ' 'follows: ' '(ASTM G-173 ETR * Standard Air Rayleigh Scattering).'), 'y_label': '', 'aspect': None, 'standalone': False} blue_sky_color = XYZ_to_sRGB(spectral_to_XYZ(spd)) single_colour_plot(colour_parameter('', normalise(blue_sky_color)), **settings) settings = {'standalone': True} settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def planckian_table(uv, cmfs, start, end, count): """ Returns a planckian table from given *CIE UCS* colourspace *uv* chromaticity coordinates, colour matching functions and temperature range using Ohno (2013) method. Parameters ---------- uv : array_like *uv* chromaticity coordinates. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. start : numeric Temperature range start in kelvins. end : numeric Temperature range end in kelvins. count : int Temperatures count in the planckian table. Returns ------- list Planckian table. Examples -------- >>> from colour import STANDARD_OBSERVERS_CMFS >>> from pprint import pprint >>> cmfs = 'CIE 1931 2 Degree Standard Observer' >>> cmfs = STANDARD_OBSERVERS_CMFS.get(cmfs) >>> uv = np.array([0.1978, 0.3122]) >>> pprint(planckian_table( # doctest: +ELLIPSIS ... uv, cmfs, 1000, 1010, 10)) [PlanckianTable_Tuvdi(Ti=1000.0, \ ui=0.4479628..., vi=0.3546296..., di=0.2537355...), PlanckianTable_Tuvdi(Ti=1001.1111111..., \ ui=0.4477030..., vi=0.3546521..., di=0.2534831...), PlanckianTable_Tuvdi(Ti=1002.2222222..., \ ui=0.4474434..., vi=0.3546746..., di=0.2532310...), PlanckianTable_Tuvdi(Ti=1003.3333333..., \ ui=0.4471842..., vi=0.3546970..., di=0.2529792...), PlanckianTable_Tuvdi(Ti=1004.4444444..., \ ui=0.4469252..., vi=0.3547194..., di=0.2527277...), PlanckianTable_Tuvdi(Ti=1005.5555555..., \ ui=0.4466666..., vi=0.3547417..., di=0.2524765...), PlanckianTable_Tuvdi(Ti=1006.6666666..., \ ui=0.4464083..., vi=0.3547640..., di=0.2522256...), PlanckianTable_Tuvdi(Ti=1007.7777777..., \ ui=0.4461502..., vi=0.3547862..., di=0.2519751...), PlanckianTable_Tuvdi(Ti=1008.8888888..., \ ui=0.4458925..., vi=0.3548084..., di=0.2517248...), PlanckianTable_Tuvdi(Ti=1010.0, \ ui=0.4456351..., vi=0.3548306..., di=0.2514749...)] """ ux, vx = uv shape = cmfs.shape table = [] for Ti in np.linspace(start, end, count): spd = blackbody_spd(Ti, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) ui, vi = UCS_to_uv(UVW) di = np.sqrt((ux - ui) ** 2 + (vx - vi) ** 2) table.append(PLANCKIAN_TABLE_TUVD(Ti, ui, vi, di)) return table
def spds_CIE_1976_UCS_chromaticity_diagram_plot( spds, cmfs='CIE 1931 2 Degree Standard Observer', annotate=True, **kwargs): """ Plots given spectral power distribution chromaticity coordinates into the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- spds : array_like, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. annotate : bool Should resulting chromaticity coordinates annotated with their respective spectral power distribution names. \**kwargs : dict, optional Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> A = ILLUMINANTS_RELATIVE_SPDS['A'] >>> D65 = ILLUMINANTS_RELATIVE_SPDS['D65'] >>> spds_CIE_1976_UCS_chromaticity_diagram_plot([A, D65]) # doctest: +SKIP True """ settings = {} settings.update(kwargs) settings.update({'standalone': False}) CIE_1976_UCS_chromaticity_diagram_plot(**settings) cmfs = get_cmfs(cmfs) cmfs_shape = cmfs.shape for spd in spds: spd = spd.clone().align(cmfs_shape) XYZ = spectral_to_XYZ(spd) / 100 uv = Luv_to_uv(XYZ_to_Luv(XYZ)) pylab.plot(uv[0], uv[1], 'o', color='white') if spd.name is not None and annotate: pylab.annotate(spd.name, xy=uv, xytext=(50, 30), textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=0.2')) settings.update({ 'x_tighten': True, 'y_tighten': True, 'limits': (-0.1, 0.7, -0.1, 0.7), 'standalone': True }) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def the_blue_sky_plot(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the blue sky. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions. 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. Examples -------- >>> the_blue_sky_plot() # doctest: +SKIP """ canvas(**kwargs) cmfs, name = get_cmfs(cmfs), cmfs ASTM_G_173_spd = ASTM_G_173_ETR.copy() rayleigh_spd = rayleigh_scattering_spd() ASTM_G_173_spd.align(rayleigh_spd.shape) spd = rayleigh_spd * ASTM_G_173_spd matplotlib.pyplot.subplots_adjust(hspace=0.4) matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(211) settings = { 'title': 'The Blue Sky - Synthetic Spectral Power Distribution', 'y_label': u'W / m-2 / nm-1', 'standalone': False } settings.update(kwargs) single_spd_plot(spd, name, **settings) matplotlib.pyplot.subplot(212) settings = { 'title': 'The Blue Sky - Colour', 'x_label': ('The sky is blue because molecules in the atmosphere ' 'scatter shorter wavelengths more than longer ones.\n' 'The synthetic spectral power distribution is computed as ' 'follows: ' '(ASTM G-173 ETR * Standard Air Rayleigh Scattering).'), 'y_label': '', 'aspect': None, 'standalone': False } blue_sky_color = XYZ_to_sRGB(spectral_to_XYZ(spd)) single_colour_swatch_plot( ColourSwatch('', normalise_maximum(blue_sky_color)), **settings) settings = {'standalone': True} settings.update(kwargs) return render(**settings)
def multi_spd_plot(spds, cmfs='CIE 1931 2 Degree Standard Observer', use_spds_colours=False, normalise_spds_colours=False, **kwargs): """ Plots given spectral power distributions. Parameters ---------- spds : list Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for spectrum creation. use_spds_colours : bool, optional Use spectral power distributions colours. normalise_spds_colours : bool Should spectral power distributions colours normalised. \**kwargs : dict, optional Keywords arguments. Returns ------- Figure Current figure or None. Examples -------- >>> from colour import SpectralPowerDistribution >>> data1 = {400: 0.0641, 420: 0.0645, 440: 0.0562} >>> data2 = {400: 0.134, 420: 0.789, 440: 1.289} >>> spd1 = SpectralPowerDistribution('Custom1', data1) >>> spd2 = SpectralPowerDistribution('Custom2', data2) >>> multi_spd_plot([spd1, spd2]) # doctest: +SKIP """ canvas(**kwargs) cmfs = get_cmfs(cmfs) if use_spds_colours: illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D65') x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] for spd in spds: wavelengths, values = tuple(zip(*spd.items)) shape = spd.shape x_limit_min.append(shape.start) x_limit_max.append(shape.end) y_limit_min.append(min(values)) y_limit_max.append(max(values)) if use_spds_colours: XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100 if normalise_spds_colours: XYZ = normalise_maximum(XYZ, clip=False) RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1) pylab.plot(wavelengths, values, color=RGB, label=spd.title, linewidth=2) else: pylab.plot(wavelengths, values, label=spd.title, linewidth=2) settings = { 'x_label': 'Wavelength $\\lambda$ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True, 'legend': True, 'legend_location': 'upper left', 'limits': (min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max)) } settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def colour_rendering_index(spd_test, additional_data=False): """ Returns the *Colour Rendering Index* (CRI) :math:`Q_a` of given spectral power distribution. Parameters ---------- spd_test : SpectralPowerDistribution Test spectral power distribution. additional_data : bool, optional Output additional data. Returns ------- numeric or CRI_Specification *Colour Rendering Index* (CRI). Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> spd = ILLUMINANTS_RELATIVE_SPDS['F2'] >>> colour_rendering_index(spd) # doctest: +ELLIPSIS 64.1515202... """ cmfs = STANDARD_OBSERVERS_CMFS[ 'CIE 1931 2 Degree Standard Observer'].clone().trim_wavelengths( ASTME30815_PRACTISE_SHAPE) shape = cmfs.shape spd_test = spd_test.clone().align(shape) tcs_spds = { spd.name: spd.clone().align(shape) for spd in TCS_SPDS.values() } XYZ = spectral_to_XYZ(spd_test, cmfs) uv = UCS_to_uv(XYZ_to_UCS(XYZ)) CCT, _D_uv = uv_to_CCT_Robertson1968(uv) if CCT < 5000: spd_reference = blackbody_spd(CCT, shape) else: xy = CCT_to_xy_CIE_D(CCT) spd_reference = D_illuminant_relative_spd(xy) spd_reference.align(shape) test_tcs_colorimetry_data = tcs_colorimetry_data( spd_test, spd_reference, tcs_spds, cmfs, chromatic_adaptation=True) reference_tcs_colorimetry_data = tcs_colorimetry_data( spd_reference, spd_reference, tcs_spds, cmfs) Q_as = colour_rendering_indexes(test_tcs_colorimetry_data, reference_tcs_colorimetry_data) Q_a = np.average( [v.Q_a for k, v in Q_as.items() if k in (1, 2, 3, 4, 5, 6, 7, 8)]) if additional_data: return CRI_Specification(spd_test.name, Q_a, Q_as, (test_tcs_colorimetry_data, reference_tcs_colorimetry_data)) else: return Q_a
def blackbody_spectral_radiance_plot( temperature=3500, cmfs='CIE 1931 2 Degree Standard Observer', blackbody='VY Canis Major', **kwargs): """ Plots given blackbody spectral radiance. Parameters ---------- temperature : numeric, optional Blackbody temperature. cmfs : unicode, optional Standard observer colour matching functions. blackbody : unicode, optional Blackbody name. \**kwargs : dict, optional Keywords arguments. Returns ------- Figure Current figure or None. Examples -------- >>> blackbody_spectral_radiance_plot() # doctest: +SKIP """ canvas(**kwargs) cmfs = get_cmfs(cmfs) matplotlib.pyplot.subplots_adjust(hspace=0.4) spd = blackbody_spd(temperature, cmfs.shape) matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(211) settings = { 'title': '{0} - Spectral Radiance'.format(blackbody), 'y_label': 'W / (sr m$^2$) / m', 'standalone': False } settings.update(kwargs) single_spd_plot(spd, cmfs.name, **settings) XYZ = spectral_to_XYZ(spd, cmfs) RGB = normalise_maximum(XYZ_to_sRGB(XYZ / 100)) matplotlib.pyplot.subplot(212) settings = { 'title': '{0} - Colour'.format(blackbody), 'x_label': '{0}K'.format(temperature), 'y_label': '', 'aspect': None, 'standalone': False } single_colour_plot(ColourParameter(name='', RGB=RGB), **settings) settings = {'standalone': True} settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def colour_rendering_index(test_spd, additional_data=False): """ Returns the *colour rendering index* of given spectral power distribution. Parameters ---------- test_spd : SpectralPowerDistribution Test spectral power distribution. additional_data : bool, optional Output additional data. Returns ------- numeric or (numeric, dict) Colour rendering index, Tsc data. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> spd = ILLUMINANTS_RELATIVE_SPDS.get('F2') >>> colour_rendering_index(spd) # doctest: +ELLIPSIS 64.1507331... """ cmfs = STANDARD_OBSERVERS_CMFS.get('CIE 1931 2 Degree Standard Observer') shape = cmfs.shape test_spd = test_spd.clone().align(shape) tcs_spds = {} for index, tcs_spd in sorted(TCS_SPDS.items()): tcs_spds[index] = tcs_spd.clone().align(shape) XYZ = spectral_to_XYZ(test_spd, cmfs) uv = UCS_to_uv(XYZ_to_UCS(XYZ)) CCT, Duv = uv_to_CCT_robertson1968(uv) if CCT < 5000: reference_spd = blackbody_spd(CCT, shape) else: xy = CCT_to_xy_illuminant_D(CCT) reference_spd = D_illuminant_relative_spd(xy) reference_spd.align(shape) test_tcs_colorimetry_data = _tcs_colorimetry_data( test_spd, reference_spd, tcs_spds, cmfs, chromatic_adaptation=True) reference_tcs_colorimetry_data = _tcs_colorimetry_data( reference_spd, reference_spd, tcs_spds, cmfs) colour_rendering_indexes = _colour_rendering_indexes( test_tcs_colorimetry_data, reference_tcs_colorimetry_data) colour_rendering_index = np.average([ v for k, v in colour_rendering_indexes.items() if k in (1, 2, 3, 4, 5, 6, 7, 8) ]) if additional_data: return (colour_rendering_index, colour_rendering_indexes, [test_tcs_colorimetry_data, reference_tcs_colorimetry_data]) else: return colour_rendering_index
def planckian_table(uv, cmfs, start, end, count): """ Returns a planckian table from given *CIE UCS* colourspace *uv* chromaticity coordinates, colour matching functions and temperature range using *Ohno (2013)* method. Parameters ---------- uv : array_like *uv* chromaticity coordinates. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. start : numeric Temperature range start in kelvins. end : numeric Temperature range end in kelvins. count : int Temperatures count in the planckian table. Returns ------- list Planckian table. Examples -------- >>> from colour import STANDARD_OBSERVERS_CMFS >>> from pprint import pprint >>> cmfs = STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> uv = np.array([0.1978, 0.3122]) >>> pprint(planckian_table(uv, cmfs, 1000, 1010, 10)) ... # doctest: +ELLIPSIS [PlanckianTable_Tuvdi(Ti=1000.0, \ ui=0.4479628..., vi=0.3546296..., di=0.2537355...), PlanckianTable_Tuvdi(Ti=1001.1111111..., \ ui=0.4477030..., vi=0.3546521..., di=0.2534831...), PlanckianTable_Tuvdi(Ti=1002.2222222..., \ ui=0.4474434..., vi=0.3546746..., di=0.2532310...), PlanckianTable_Tuvdi(Ti=1003.3333333..., \ ui=0.4471842..., vi=0.3546970..., di=0.2529792...), PlanckianTable_Tuvdi(Ti=1004.4444444..., \ ui=0.4469252..., vi=0.3547194..., di=0.2527277...), PlanckianTable_Tuvdi(Ti=1005.5555555..., \ ui=0.4466666..., vi=0.3547417..., di=0.2524765...), PlanckianTable_Tuvdi(Ti=1006.6666666..., \ ui=0.4464083..., vi=0.3547640..., di=0.2522256...), PlanckianTable_Tuvdi(Ti=1007.7777777..., \ ui=0.4461502..., vi=0.3547862..., di=0.2519751...), PlanckianTable_Tuvdi(Ti=1008.8888888..., \ ui=0.4458925..., vi=0.3548084..., di=0.2517248...), PlanckianTable_Tuvdi(Ti=1010.0, \ ui=0.4456351..., vi=0.3548306..., di=0.2514749...)] """ ux, vx = uv cmfs = cmfs.copy().trim(ASTME30815_PRACTISE_SHAPE) shape = cmfs.shape table = [] for Ti in np.linspace(start, end, count): spd = blackbody_spd(Ti, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ /= np.max(XYZ) UVW = XYZ_to_UCS(XYZ) ui, vi = UCS_to_uv(UVW) di = np.hypot(ux - ui, vx - vi) table.append(PLANCKIAN_TABLE_TUVD(Ti, ui, vi, di)) return table
def colour_rendering_index(test_spd, additional_data=False): """ Returns the *colour rendering index* of given spectral power distribution. Parameters ---------- test_spd : SpectralPowerDistribution Test spectral power distribution. additional_data : bool, optional Output additional data. Returns ------- numeric or (numeric, dict) Colour rendering index, Tsc data. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> spd = ILLUMINANTS_RELATIVE_SPDS.get('F2') >>> colour_rendering_index(spd) # doctest: +ELLIPSIS 64.1507331... """ cmfs = STANDARD_OBSERVERS_CMFS.get('CIE 1931 2 Degree Standard Observer') shape = cmfs.shape test_spd = test_spd.clone().align(shape) tcs_spds = {} for index, tcs_spd in sorted(TCS_SPDS.items()): tcs_spds[index] = tcs_spd.clone().align(shape) XYZ = spectral_to_XYZ(test_spd, cmfs) uv = UCS_to_uv(XYZ_to_UCS(XYZ)) CCT, Duv = uv_to_CCT_robertson1968(uv) if CCT < 5000: reference_spd = blackbody_spd(CCT, shape) else: xy = CCT_to_xy_illuminant_D(CCT) reference_spd = D_illuminant_relative_spd(xy) reference_spd.align(shape) test_tcs_colorimetry_data = _tcs_colorimetry_data( test_spd, reference_spd, tcs_spds, cmfs, chromatic_adaptation=True) reference_tcs_colorimetry_data = _tcs_colorimetry_data( reference_spd, reference_spd, tcs_spds, cmfs) colour_rendering_indexes = _colour_rendering_indexes( test_tcs_colorimetry_data, reference_tcs_colorimetry_data) colour_rendering_index = np.average( [v for k, v in colour_rendering_indexes.items() if k in (1, 2, 3, 4, 5, 6, 7, 8)]) if additional_data: return (colour_rendering_index, colour_rendering_indexes, [test_tcs_colorimetry_data, reference_tcs_colorimetry_data]) else: return colour_rendering_index
def colour_quality_scale(spd_test, additional_data=False): """ Returns the *colour quality scale* of given spectral power distribution. Parameters ---------- spd_test : SpectralPowerDistribution Test spectral power distribution. additional_data : bool, optional Output additional data. Returns ------- numeric or CQS_Specification Color quality scale. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> spd = ILLUMINANTS_RELATIVE_SPDS.get('F2') >>> colour_quality_scale(spd) # doctest: +ELLIPSIS 64.6781117... """ cmfs = STANDARD_OBSERVERS_CMFS.get( 'CIE 1931 2 Degree Standard Observer') shape = cmfs.shape XYZ = spectral_to_XYZ(spd_test, cmfs) uv = UCS_to_uv(XYZ_to_UCS(XYZ)) CCT, _D_uv = uv_to_CCT_Ohno2013(uv) if CCT < 5000: spd_reference = blackbody_spd(CCT, shape) else: xy = CCT_to_xy_CIE_D(CCT) spd_reference = D_illuminant_relative_spd(xy) spd_reference.align(shape) test_vs_colorimetry_data = vs_colorimetry_data( spd_test, spd_reference, VS_SPDS, cmfs, chromatic_adaptation=True) reference_vs_colorimetry_data = vs_colorimetry_data( spd_reference, spd_reference, VS_SPDS, cmfs) XYZ_r = spectral_to_XYZ(spd_reference, cmfs) XYZ_r /= XYZ_r[1] CCT_f = CCT_factor(reference_vs_colorimetry_data, XYZ_r) Q_as = colour_quality_scales( test_vs_colorimetry_data, reference_vs_colorimetry_data, CCT_f) D_E_RMS = delta_E_RMS(Q_as, 'D_E_ab') D_Ep_RMS = delta_E_RMS(Q_as, 'D_Ep_ab') Q_a = scale_conversion(D_Ep_RMS, CCT_f) Q_f = scale_conversion(D_E_RMS, CCT_f, 2.928) p_delta_C = np.average( [sample_data.D_C_ab if sample_data.D_C_ab > 0 else 0 for sample_data in Q_as.values()]) Q_p = 100 - 3.6 * (D_Ep_RMS - p_delta_C) G_t = gamut_area([vs_CQS_data.Lab for vs_CQS_data in test_vs_colorimetry_data]) G_r = gamut_area([vs_CQS_data.Lab for vs_CQS_data in reference_vs_colorimetry_data]) Q_g = G_t / D65_GAMUT_AREA * 100 Q_d = G_t / G_r * CCT_f * 100 if additional_data: return CQS_Specification(spd_test.name, Q_a, Q_f, Q_p, Q_g, Q_d, Q_as, (test_vs_colorimetry_data, reference_vs_colorimetry_data)) else: return Q_a
def _tcs_colorimetry_data(test_spd, reference_spd, tsc_spds, cmfs, chromatic_adaptation=False): """ Returns the *test colour samples* colorimetry data. Parameters ---------- test_spd : SpectralPowerDistribution Test spectral power distribution. reference_spd : SpectralPowerDistribution Reference spectral power distribution. tsc_spds : dict Test colour samples. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. chromatic_adaptation : bool, optional Perform chromatic adaptation. Returns ------- list *Test colour samples* colorimetry data. """ test_XYZ = spectral_to_XYZ(test_spd, cmfs) test_uv = np.ravel(UCS_to_uv(XYZ_to_UCS(test_XYZ))) test_u, test_v = test_uv[0], test_uv[1] reference_XYZ = spectral_to_XYZ(reference_spd, cmfs) reference_uv = np.ravel(UCS_to_uv(XYZ_to_UCS(reference_XYZ))) reference_u, reference_v = reference_uv[0], reference_uv[1] tcs_data = [] for key, value in sorted(TCS_INDEXES_TO_NAMES.items()): tcs_spd = tsc_spds.get(value) tcs_XYZ = spectral_to_XYZ(tcs_spd, cmfs, test_spd) tcs_xyY = np.ravel(XYZ_to_xyY(tcs_XYZ)) tcs_uv = np.ravel(UCS_to_uv(XYZ_to_UCS(tcs_XYZ))) tcs_u, tcs_v = tcs_uv[0], tcs_uv[1] if chromatic_adaptation: c = lambda x, y: (4 - x - 10 * y) / y d = lambda x, y: (1.708 * y + 0.404 - 1.481 * x) / y test_c, test_d = c(test_u, test_v), d(test_u, test_v) reference_c, reference_d = (c(reference_u, reference_v), d(reference_u, reference_v)) tcs_c, tcs_d = c(tcs_u, tcs_v), d(tcs_u, tcs_v) tcs_u = ((10.872 + 0.404 * reference_c / test_c * tcs_c - 4 * reference_d / test_d * tcs_d) / (16.518 + 1.481 * reference_c / test_c * tcs_c - reference_d / test_d * tcs_d)) tcs_v = (5.52 / (16.518 + 1.481 * reference_c / test_c * tcs_c - reference_d / test_d * tcs_d)) tcs_W = 25 * tcs_xyY[-1] ** (1 / 3) - 17 tcs_U = 13 * tcs_W * (tcs_u - reference_u) tcs_V = 13 * tcs_W * (tcs_v - reference_v) tcs_data.append( TSC_COLORIMETRY_DATA_NXYZUVUVW(tcs_spd.name, tcs_XYZ, tcs_uv, np.array([tcs_U, tcs_V, tcs_W]))) return tcs_data
def tcs_colorimetry_data(spd_t, spd_r, spds_tcs, cmfs, chromatic_adaptation=False): """ Returns the *test colour samples* colorimetry data. Parameters ---------- spd_t : SpectralPowerDistribution Test spectral power distribution. spd_r : SpectralPowerDistribution Reference spectral power distribution. spds_tcs : dict *Test colour samples* spectral power distributions. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. chromatic_adaptation : bool, optional Perform chromatic adaptation. Returns ------- list *Test colour samples* colorimetry data. """ XYZ_t = spectral_to_XYZ(spd_t, cmfs) uv_t = UCS_to_uv(XYZ_to_UCS(XYZ_t)) u_t, v_t = uv_t[0], uv_t[1] XYZ_r = spectral_to_XYZ(spd_r, cmfs) uv_r = UCS_to_uv(XYZ_to_UCS(XYZ_r)) u_r, v_r = uv_r[0], uv_r[1] tcs_data = [] for _key, value in sorted(TCS_INDEXES_TO_NAMES.items()): spd_tcs = spds_tcs[value] XYZ_tcs = spectral_to_XYZ(spd_tcs, cmfs, spd_t) xyY_tcs = XYZ_to_xyY(XYZ_tcs) uv_tcs = UCS_to_uv(XYZ_to_UCS(XYZ_tcs)) u_tcs, v_tcs = uv_tcs[0], uv_tcs[1] if chromatic_adaptation: def c(x, y): """ Computes the :math:`c` term. """ return (4 - x - 10 * y) / y def d(x, y): """ Computes the :math:`d` term. """ return (1.708 * y + 0.404 - 1.481 * x) / y c_t, d_t = c(u_t, v_t), d(u_t, v_t) c_r, d_r = c(u_r, v_r), d(u_r, v_r) tcs_c, tcs_d = c(u_tcs, v_tcs), d(u_tcs, v_tcs) u_tcs = (( 10.872 + 0.404 * c_r / c_t * tcs_c - 4 * d_r / d_t * tcs_d) / (16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d)) v_tcs = (5.52 / (16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d)) W_tcs = 25 * xyY_tcs[-1] ** (1 / 3) - 17 U_tcs = 13 * W_tcs * (u_tcs - u_r) V_tcs = 13 * W_tcs * (v_tcs - v_r) tcs_data.append( TCS_ColorimetryData(spd_tcs.name, XYZ_tcs, uv_tcs, np.array([U_tcs, V_tcs, W_tcs]))) return tcs_data
def CCT_to_uv_Ohno2013(CCT, D_uv=0, cmfs=STANDARD_OBSERVERS_CMFS.get( 'CIE 1931 2 Degree Standard Observer')): """ Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}`, :math:`\Delta_{uv}` and colour matching functions using Ohno (2013) method. Parameters ---------- CCT : numeric Correlated colour temperature :math:`T_{cp}`. D_uv : numeric, optional :math:`\Delta_{uv}`. cmfs : XYZ_ColourMatchingFunctions, optional Standard observer colour matching functions. Returns ------- ndarray *CIE UCS* colourspace *uv* chromaticity coordinates. References ---------- .. [4] Ohno, Y. (2014). Practical Use and Calculation of CCT and Duv. LEUKOS, 10(1), 47–55. doi:10.1080/15502724.2014.839020 Examples -------- >>> from colour import STANDARD_OBSERVERS_CMFS >>> cmfs = 'CIE 1931 2 Degree Standard Observer' >>> cmfs = STANDARD_OBSERVERS_CMFS.get(cmfs) >>> CCT = 6507.4342201047066 >>> D_uv = 0.003223690901512735 >>> CCT_to_uv_Ohno2013(CCT, D_uv, cmfs) # doctest: +ELLIPSIS array([ 0.1978003..., 0.3122005...]) """ shape = cmfs.shape delta = 0.01 spd = blackbody_spd(CCT, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) u0, v0 = UCS_to_uv(UVW) if D_uv == 0: return np.array([u0, v0]) else: spd = blackbody_spd(CCT + delta, shape) XYZ = spectral_to_XYZ(spd, cmfs) XYZ *= 1 / np.max(XYZ) UVW = XYZ_to_UCS(XYZ) u1, v1 = UCS_to_uv(UVW) du = u0 - u1 dv = v0 - v1 u = u0 - D_uv * (dv / np.sqrt(du ** 2 + dv ** 2)) v = v0 + D_uv * (du / np.sqrt(du ** 2 + dv ** 2)) return np.array([u, v])
def multi_spd_plot(spds, cmfs='CIE 1931 2 Degree Standard Observer', use_spds_colours=False, normalise_spds_colours=False, **kwargs): """ Plots given spectral power distributions. Parameters ---------- spds : list, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for spectrum creation. use_spds_colours : bool, optional Use spectral power distributions colours. normalise_spds_colours : bool Should spectral power distributions colours normalised. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import SpectralPowerDistribution >>> data1 = {400: 0.0641, 420: 0.0645, 440: 0.0562} >>> data2 = {400: 0.134, 420: 0.789, 440: 1.289} >>> spd1 = SpectralPowerDistribution('Custom1', data1) >>> spd2 = SpectralPowerDistribution('Custom2', data2) >>> multi_spd_plot([spd1, spd2]) # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs if use_spds_colours: illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D65') x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] for spd in spds: wavelengths, values = tuple(zip(*[(key, value) for key, value in spd])) shape = spd.shape x_limit_min.append(shape.start) x_limit_max.append(shape.end) y_limit_min.append(min(values)) y_limit_max.append(max(values)) matplotlib.pyplot.rc("axes", color_cycle=["r", "g", "b", "y"]) if use_spds_colours: XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100 if normalise_spds_colours: XYZ = normalise(XYZ, clip=False) RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1) pylab.plot(wavelengths, values, color=RGB, label=spd.name, linewidth=2) else: pylab.plot(wavelengths, values, label=spd.name, linewidth=2) settings = { 'x_label': u'Wavelength λ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True, 'legend': True, 'legend_location': 'upper left', 'x_ticker': True, 'y_ticker': True, 'limits': [ min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max) ] } settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def spds_chromaticity_diagram_plot_CIE1931( spds, cmfs='CIE 1931 2 Degree Standard Observer', annotate=True, chromaticity_diagram_callable_CIE1931=( chromaticity_diagram_plot_CIE1931), **kwargs): """ Plots given spectral power distribution chromaticity coordinates into the *CIE 1931 Chromaticity Diagram*. Parameters ---------- spds : array_like, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. annotate : bool Should resulting chromaticity coordinates annotated with their respective spectral power distribution names. chromaticity_diagram_callable_CIE1931 : callable, optional Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*. Other Parameters ---------------- \**kwargs : dict, optional {:func:`colour.plotting.render`}, Please refer to the documentation of the previously listed definition. show_diagram_colours : bool, optional {:func:`colour.plotting.chromaticity_diagram_plot_CIE1931`}, Whether to display the chromaticity diagram background colours. use_cached_diagram_colours : bool, optional {:func:`colour.plotting.chromaticity_diagram_plot_CIE1931`}, Whether to used the cached chromaticity diagram background colours image. Returns ------- Figure Current figure or None. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> A = ILLUMINANTS_RELATIVE_SPDS['A'] >>> D65 = ILLUMINANTS_RELATIVE_SPDS['D65'] >>> spds_chromaticity_diagram_plot_CIE1931([A, D65]) # doctest: +SKIP """ settings = {} settings.update(kwargs) settings.update({'cmfs': cmfs, 'standalone': False}) chromaticity_diagram_callable_CIE1931(**settings) for spd in spds: XYZ = spectral_to_XYZ(spd) / 100 xy = XYZ_to_xy(XYZ) pylab.plot(xy[0], xy[1], 'o', color='white') if spd.name is not None and annotate: pylab.annotate(spd.name, xy=xy, xytext=(50, 30), color='black', textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=0.2')) settings.update({ 'x_tighten': True, 'y_tighten': True, 'limits': (-0.1, 0.9, -0.1, 0.9), 'standalone': True }) settings.update(kwargs) return render(**settings)
def colour_rendering_index(spd_test, additional_data=False): """ Returns the *colour rendering index* :math:`Q_a` of given spectral power distribution. Parameters ---------- spd_test : SpectralPowerDistribution Test spectral power distribution. additional_data : bool, optional Output additional data. Returns ------- numeric or CRI_Specification Colour rendering index. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> spd = ILLUMINANTS_RELATIVE_SPDS.get('F2') >>> colour_rendering_index(spd) # doctest: +ELLIPSIS 64.1507331... """ cmfs = STANDARD_OBSERVERS_CMFS.get('CIE 1931 2 Degree Standard Observer') shape = cmfs.shape spd_test = spd_test.clone().align(shape) tcs_spds = {} for index, tcs_spd in TCS_SPDS.items(): tcs_spds[index] = tcs_spd.clone().align(shape) XYZ = spectral_to_XYZ(spd_test, cmfs) uv = UCS_to_uv(XYZ_to_UCS(XYZ)) CCT, _D_uv = uv_to_CCT_Robertson1968(uv) if CCT < 5000: spd_reference = blackbody_spd(CCT, shape) else: xy = CCT_to_xy_CIE_D(CCT) spd_reference = D_illuminant_relative_spd(xy) spd_reference.align(shape) test_tcs_colorimetry_data = tcs_colorimetry_data( spd_test, spd_reference, tcs_spds, cmfs, chromatic_adaptation=True) reference_tcs_colorimetry_data = tcs_colorimetry_data( spd_reference, spd_reference, tcs_spds, cmfs) Q_as = colour_rendering_indexes( test_tcs_colorimetry_data, reference_tcs_colorimetry_data) Q_a = np.average([v.Q_a for k, v in Q_as.items() if k in (1, 2, 3, 4, 5, 6, 7, 8)]) if additional_data: return CRI_Specification(spd_test.name, Q_a, Q_as, (test_tcs_colorimetry_data, reference_tcs_colorimetry_data)) else: return Q_a
def spds_CIE_1931_chromaticity_diagram_plot( spds, cmfs='CIE 1931 2 Degree Standard Observer', annotate=True, **kwargs): """ Plots given spectral power distribution chromaticity coordinates into the *CIE 1931 Chromaticity Diagram*. Parameters ---------- spds : array_like, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. annotate : bool Should resulting chromaticity coordinates annotated with their respective spectral power distribution names. \**kwargs : dict, optional Keywords arguments. Returns ------- Figure Current figure or None. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> A = ILLUMINANTS_RELATIVE_SPDS['A'] >>> D65 = ILLUMINANTS_RELATIVE_SPDS['D65'] >>> spds_CIE_1931_chromaticity_diagram_plot([A, D65]) # doctest: +SKIP """ settings = {} settings.update(kwargs) settings.update({'standalone': False}) CIE_1931_chromaticity_diagram_plot(cmfs=cmfs, **settings) for spd in spds: XYZ = spectral_to_XYZ(spd) / 100 xy = XYZ_to_xy(XYZ) pylab.plot(xy[0], xy[1], 'o', color='white') if spd.name is not None and annotate: pylab.annotate(spd.name, xy=xy, xytext=(50, 30), color='black', textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=0.2')) settings.update({ 'x_tighten': True, 'y_tighten': True, 'limits': (-0.1, 0.9, -0.1, 0.9), 'standalone': True}) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def colour_quality_scale(spd_test, additional_data=False): """ Returns the *Colour Quality Scale* (CQS) of given spectral power distribution. Parameters ---------- spd_test : SpectralPowerDistribution Test spectral power distribution. additional_data : bool, optional Output additional data. Returns ------- numeric or CQS_Specification Color quality scale. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> spd = ILLUMINANTS_RELATIVE_SPDS['F2'] >>> colour_quality_scale(spd) # doctest: +ELLIPSIS 64.6864169... """ cmfs = STANDARD_OBSERVERS_CMFS[ 'CIE 1931 2 Degree Standard Observer'].clone().trim_wavelengths( ASTME30815_PRACTISE_SHAPE) shape = cmfs.shape spd_test = spd_test.clone().align(shape) vs_spds = {spd.name: spd.clone().align(shape) for spd in VS_SPDS.values()} XYZ = spectral_to_XYZ(spd_test, cmfs) uv = UCS_to_uv(XYZ_to_UCS(XYZ)) CCT, _D_uv = uv_to_CCT_Ohno2013(uv) if CCT < 5000: spd_reference = blackbody_spd(CCT, shape) else: xy = CCT_to_xy_CIE_D(CCT) spd_reference = D_illuminant_relative_spd(xy) spd_reference.align(shape) test_vs_colorimetry_data = vs_colorimetry_data(spd_test, spd_reference, vs_spds, cmfs, chromatic_adaptation=True) reference_vs_colorimetry_data = vs_colorimetry_data( spd_reference, spd_reference, vs_spds, cmfs) XYZ_r = spectral_to_XYZ(spd_reference, cmfs) XYZ_r /= XYZ_r[1] CCT_f = CCT_factor(reference_vs_colorimetry_data, XYZ_r) Q_as = colour_quality_scales(test_vs_colorimetry_data, reference_vs_colorimetry_data, CCT_f) D_E_RMS = delta_E_RMS(Q_as, 'D_E_ab') D_Ep_RMS = delta_E_RMS(Q_as, 'D_Ep_ab') Q_a = scale_conversion(D_Ep_RMS, CCT_f) Q_f = scale_conversion(D_E_RMS, CCT_f, 2.928) p_delta_C = np.average( [sample_data.D_C_ab if sample_data.D_C_ab > 0 else 0 for sample_data in Q_as.values()]) # yapf: disable Q_p = 100 - 3.6 * (D_Ep_RMS - p_delta_C) G_t = gamut_area( [vs_CQS_data.Lab for vs_CQS_data in test_vs_colorimetry_data]) G_r = gamut_area( [vs_CQS_data.Lab for vs_CQS_data in reference_vs_colorimetry_data]) Q_g = G_t / D65_GAMUT_AREA * 100 Q_d = G_t / G_r * CCT_f * 100 if additional_data: return CQS_Specification( spd_test.name, Q_a, Q_f, Q_p, Q_g, Q_d, Q_as, (test_vs_colorimetry_data, reference_vs_colorimetry_data)) else: return Q_a
def spds_CIE_1931_chromaticity_diagram_plot( spds, cmfs='CIE 1931 2 Degree Standard Observer', annotate=True, **kwargs): """ Plots given spectral power distribution chromaticity coordinates into the *CIE 1931 Chromaticity Diagram*. Parameters ---------- spds : array_like, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. annotate : bool Should resulting chromaticity coordinates annotated with their respective spectral power distribution names. \**kwargs : dict, optional Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> A = ILLUMINANTS_RELATIVE_SPDS['A'] >>> D65 = ILLUMINANTS_RELATIVE_SPDS['D65'] >>> spds_CIE_1931_chromaticity_diagram_plot([A, D65]) # doctest: +SKIP True """ settings = {} settings.update(kwargs) settings.update({'standalone': False}) CIE_1931_chromaticity_diagram_plot(**settings) cmfs = get_cmfs(cmfs) cmfs_shape = cmfs.shape for spd in spds: spd = spd.clone().align(cmfs_shape) XYZ = spectral_to_XYZ(spd) / 100 xy = XYZ_to_xy(XYZ) pylab.plot(xy[0], xy[1], 'o', color='white') if spd.name is not None and annotate: pylab.annotate(spd.name, xy=xy, xytext=(50, 30), textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=0.2')) settings.update({ 'x_tighten': True, 'y_tighten': True, 'limits': (-0.1, 0.9, -0.1, 0.9), 'standalone': True}) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def spds_CIE_1976_UCS_chromaticity_diagram_plot( spds, cmfs='CIE 1931 2 Degree Standard Observer', annotate=True, **kwargs): """ Plots given spectral power distribution chromaticity coordinates into the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- spds : array_like, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. annotate : bool Should resulting chromaticity coordinates annotated with their respective spectral power distribution names. Other Parameters ---------------- \**kwargs : dict, optional {:func:`boundaries`, :func:`canvas`, :func:`decorate`, :func:`display`}, Please refer to the documentation of the previously listed definitions. show_diagram_colours : bool, optional {:func:`CIE_1976_UCS_chromaticity_diagram_plot`}, Whether to display the chromaticity diagram background colours. Returns ------- Figure Current figure or None. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> A = ILLUMINANTS_RELATIVE_SPDS['A'] >>> D65 = ILLUMINANTS_RELATIVE_SPDS['D65'] >>> spds_CIE_1976_UCS_chromaticity_diagram_plot([A, D65]) # doctest: +SKIP """ settings = {} settings.update(kwargs) settings.update({'standalone': False}) CIE_1976_UCS_chromaticity_diagram_plot(cmfs=cmfs, **settings) for spd in spds: XYZ = spectral_to_XYZ(spd) / 100 uv = Luv_to_uv(XYZ_to_Luv(XYZ)) pylab.plot(uv[0], uv[1], 'o', color='white') if spd.name is not None and annotate: pylab.annotate(spd.name, xy=uv, xytext=(50, 30), color='black', textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=0.2')) settings.update({ 'x_tighten': True, 'y_tighten': True, 'limits': (-0.1, 0.7, -0.1, 0.7), 'standalone': True }) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def blackbody_colours_plot(shape=SpectralShape(150, 12500, 50), cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots blackbody colours. Parameters ---------- shape : SpectralShape, optional Spectral shape to use as plot boundaries. cmfs : unicode, optional Standard observer colour matching functions. Other Parameters ---------------- \**kwargs : dict, optional {:func:`boundaries`, :func:`canvas`, :func:`decorate`, :func:`display`}, Please refer to the documentation of the previously listed definitions. y0_plot : bool, optional {:func:`colour_parameters_plot`}, Whether to plot *y0* line. y1_plot : bool, optional {:func:`colour_parameters_plot`}, Whether to plot *y1* line. Returns ------- Figure Current figure or None. Examples -------- >>> blackbody_colours_plot() # doctest: +SKIP """ cmfs = get_cmfs(cmfs) colours = [] temperatures = [] for temperature in shape: spd = blackbody_spd(temperature, cmfs.shape) XYZ = spectral_to_XYZ(spd, cmfs) RGB = normalise_maximum(XYZ_to_sRGB(XYZ / 100)) colours.append(RGB) temperatures.append(temperature) settings = { 'title': 'Blackbody Colours', 'x_label': 'Temperature K', 'y_label': '', 'x_tighten': True, 'y_tighten': True, 'y_ticker': False } settings.update(kwargs) return colour_parameters_plot([ ColourParameter(x=x[0], RGB=x[1]) for x in tuple(zip(temperatures, colours)) ], **settings)