def test_n_dimensional_UCS_to_uv(self): """ Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition n-dimensions support. """ UCS = np.array([0.04699689, 0.10080000, 0.16374390]) uv = np.array([0.15085309, 0.32355314]) np.testing.assert_almost_equal( UCS_to_uv(UCS), uv, decimal=7) UCS = np.tile(UCS, (6, 1)) uv = np.tile(uv, (6, 1)) np.testing.assert_almost_equal( UCS_to_uv(UCS), uv, decimal=7) UCS = np.reshape(UCS, (2, 3, 3)) uv = np.reshape(uv, (2, 3, 2)) np.testing.assert_almost_equal( UCS_to_uv(UCS), uv, decimal=7)
def XYZ_to_UVW( XYZ, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D50']): """ Converts from *CIE XYZ* tristimulus values to *CIE 1964 U\*V\*W\** colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. illuminant : array_like, optional Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE 1964 U\*V\*W\** colourspace array. Warning ------- The input domain and output range of that definition are non standard! Notes ----- - Input *CIE XYZ* tristimulus values are in domain [0, 100]. - Input *illuminant* *xy* chromaticity coordinates or *CIE xyY* colourspace array are in domain [0, :math:`\infty`]. - Output *CIE 1964 U\*V\*W\** colourspace array is in range [0, 100]. References ---------- - :cite:`Wikipediacj` Examples -------- >>> import numpy as np >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313]) * 100 >>> XYZ_to_UVW(XYZ) # doctest: +ELLIPSIS array([-28.0579733..., -0.8819449..., 37.0041149...]) """ xyY = XYZ_to_xyY(XYZ, xyY_to_xy(illuminant)) _x, _y, Y = tsplit(xyY) u, v = tsplit(UCS_to_uv(XYZ_to_UCS(XYZ))) u_0, v_0 = tsplit(UCS_to_uv(XYZ_to_UCS(xyY_to_XYZ(xy_to_xyY(illuminant))))) W = 25 * Y**(1 / 3) - 17 U = 13 * W * (u - u_0) V = 13 * W * (v - v_0) UVW = tstack((U, V, W)) return UVW
def _CCT_to_uv_Ohno2013( CCT_D_uv, cmfs=MSDS_CMFS_STANDARD_OBSERVER['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_D_uv : ndarray Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. cmfs : XYZ_ColourMatchingFunctions, optional Standard observer colour matching functions. Returns ------- ndarray *CIE UCS* colourspace *uv* chromaticity coordinates. """ CCT, D_uv = tsplit(CCT_D_uv) cmfs = cmfs.copy().trim(SPECTRAL_SHAPE_DEFAULT) shape = cmfs.shape delta = 0.01 sd = sd_blackbody(CCT, shape) XYZ = sd_to_XYZ(sd, 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: sd = sd_blackbody(CCT + delta, shape) XYZ = sd_to_XYZ(sd, 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 _CCT_to_uv_Ohno2013( CCT_D_uv: ArrayLike, cmfs: Optional[MultiSpectralDistributions] = None) -> NDArray: """ Return 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_D_uv Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. cmfs Standard observer colour matching functions, default to the *CIE 1931 2 Degree Standard Observer*. Returns ------- :class:`numpy.ndarray` *CIE UCS* colourspace *uv* chromaticity coordinates. """ CCT, D_uv = tsplit(CCT_D_uv) cmfs, _illuminant = handle_spectral_arguments(cmfs) delta = 0.01 sd = sd_blackbody(CCT, cmfs.shape) XYZ = sd_to_XYZ(sd, 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: sd = sd_blackbody(CCT + delta, cmfs.shape) XYZ = sd_to_XYZ(sd, 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 test_domain_range_scale_UCS_to_uv(self): """ Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition domain and range scale support. """ UCS = np.array([0.0469968933, 0.1008000000, 0.1637438950]) uv = UCS_to_uv(UCS) d_r = (('reference', 1), (1, 1), (100, 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal(UCS_to_uv(UCS * factor), uv, decimal=7)
def test_n_dimensional_UCS_to_uv(self): """ Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition n-dimensional support. """ UCS = np.array([0.13769339, 0.12197225, 0.10537310]) uv = UCS_to_uv(UCS) UCS = np.tile(UCS, (6, 1)) uv = np.tile(uv, (6, 1)) np.testing.assert_almost_equal(UCS_to_uv(UCS), uv, decimal=7) UCS = np.reshape(UCS, (2, 3, 3)) uv = np.reshape(uv, (2, 3, 2)) np.testing.assert_almost_equal(UCS_to_uv(UCS), uv, decimal=7)
def XYZ_to_ij(XYZ): """ Converts given *CIE XYZ* tristimulus values to *ij* chromaticity coordinates. """ return UCS_to_uv(XYZ_to_UCS(XYZ))
def xy_to_ij(xy): """ Converts given *CIE xy* chromaticity coordinates to *ij* chromaticity coordinates. """ return UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy)))
def CCT_reference_illuminant(sd: SpectralDistribution) -> NDArray: """ Compute the reference illuminant correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` for given test spectral distribution using *Ohno (2013)* method. Parameters ---------- sd Test spectral distribution. Returns ------- :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. Examples -------- >>> from colour import SDS_ILLUMINANTS >>> sd = SDS_ILLUMINANTS['FL2'] >>> CCT_reference_illuminant(sd) # doctest: +ELLIPSIS array([ 4.2244697...e+03, 1.7871111...e-03]) """ XYZ = sd_to_XYZ(sd) return uv_to_CCT_Ohno2013(UCS_to_uv(XYZ_to_UCS(XYZ)))
def CCT_reference_illuminant(sd): """ Computes the reference illuminant correlated colour temperature :math:`T_{cp}` and :math:`\\Delta_{uv}` for given test spectral distribution using *Ohno (2013)* method. Parameters ---------- sd : SpectralDistribution Test spectral distribution. Returns ------- ndarray Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. Examples -------- >>> from colour import SDS_ILLUMINANTS >>> sd = SDS_ILLUMINANTS['FL2'] >>> CCT_reference_illuminant(sd) # doctest: +ELLIPSIS (4224.4697052..., 0.0017871...) """ XYZ = sd_to_XYZ(sd) CCT, D_uv = uv_to_CCT_Ohno2013(UCS_to_uv(XYZ_to_UCS(XYZ))) return CCT, D_uv
def test_nan_UCS_to_uv(self): """Test :func:`colour.models.cie_ucs.UCS_to_uv` definition nan support.""" cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: UCS = np.array(case) UCS_to_uv(UCS)
def chromaticity_diagram_colours_CIE1960UCS( samples=4096, cmfs='CIE 1931 2 Degree Standard Observer', antialiasing=True): """ Plots the *CIE 1960 UCS Chromaticity Diagram* colours. Parameters ---------- samples : numeric, optional Samples count on one axis. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. antialiasing : bool, optional Whether to apply anti-aliasing to the image. 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 -------- >>> chromaticity_diagram_colours_CIE1960UCS() # doctest: +SKIP """ cmfs = get_cmfs(cmfs) illuminant = DEFAULT_PLOTTING_ILLUMINANT triangulation = Delaunay(UCS_to_uv(XYZ_to_UCS(cmfs.values)), qhull_options='Qu QJ') xx, yy = np.meshgrid(np.linspace(0, 1, samples), np.linspace(1, 0, samples)) xy = tstack((xx, yy)) mask = (triangulation.find_simplex(xy) < 0).astype(DEFAULT_FLOAT_DTYPE) if antialiasing: kernel = np.array([ [0, 1, 0], [1, 2, 1], [0, 1, 0], ]).astype(DEFAULT_FLOAT_DTYPE) kernel /= np.sum(kernel) mask = convolve(mask, kernel) mask = 1 - mask[:, :, np.newaxis] XYZ = xy_to_XYZ(UCS_uv_to_xy(xy)) RGB = normalise_maximum(XYZ_to_sRGB(XYZ, illuminant), axis=-1) return np.dstack([RGB, mask])
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 RGB_chromaticity_coordinates_CIE_1960_UCS_chromaticity_diagram_plot( RGB, colourspace, **kwargs): """ Plots given *RGB* colourspace array in *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- RGB : array_like *RGB* colourspace array. colourspace : RGB_Colourspace *RGB* colourspace of the *RGB* array. \**kwargs : dict, optional Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> RGB = np.random.random((10, 10, 3)) >>> c = 'Rec. 709' >>> RGB_chromaticity_coordinates_CIE_1960_UCS_chromaticity_diagram_plot( ... RGB, c) # doctest: +SKIP True """ settings = {} settings.update(kwargs) settings.update({'standalone': False}) settings['colourspaces'] = ( [colourspace.name] + settings.get('colourspaces', [])) RGB_colourspaces_CIE_1960_UCS_chromaticity_diagram_plot(**settings) alpha_p, colour_p = 0.85, 'black' uv = UCS_to_uv(XYZ_to_UCS(RGB_to_XYZ(RGB, colourspace.whitepoint, colourspace.whitepoint, colourspace.RGB_to_XYZ_matrix))) pylab.scatter(uv[..., 0], uv[..., 1], alpha=alpha_p / 2, color=colour_p, marker='+') settings.update({'standalone': True}) boundaries(**settings) decorate(**settings) return display(**settings)
def test_UCS_to_uv(self): """ Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition. """ np.testing.assert_almost_equal(UCS_to_uv( np.array([0.13769339, 0.12197225, 0.10537310])), np.array([0.37720213, 0.33413508]), decimal=7) np.testing.assert_almost_equal(UCS_to_uv( np.array([0.09481340, 0.23042768, 0.32701033])), np.array([0.14536327, 0.35328046]), decimal=7) np.testing.assert_almost_equal(UCS_to_uv( np.array([0.05212520, 0.06157201, 0.19376075])), np.array([0.16953602, 0.20026156]), decimal=7)
def XYZ_to_UVW(XYZ, illuminant=ILLUMINANTS.get( 'CIE 1931 2 Degree Standard Observer').get('D50')): """ Converts from *CIE XYZ* colourspace to *CIE 1964 U\*V*\W\** colourspace. Parameters ---------- XYZ : array_like, (3,) *CIE XYZ* colourspace matrix. illuminant : array_like, optional Reference *illuminant* chromaticity coordinates. Returns ------- ndarray, (3,) *CIE 1964 U\*V*\W\** colourspace matrix. Notes ----- - Input *CIE XYZ* colourspace matrix is in domain [0, 100]. - Output *CIE UVW* colourspace matrix is in domain [0, 100]. Warning ------- The input / output domains of that definition are non standard! Examples -------- >>> XYZ = np.array([11.80583421, 10.34, 5.15089229]) >>> XYZ_to_UVW(XYZ) # doctest: +ELLIPSIS array([ 24.2543371..., 7.2205484..., 37.4645000...]) """ x, y, Y = np.ravel(XYZ_to_xyY(XYZ, illuminant)) u, v = np.ravel(UCS_to_uv(XYZ_to_UCS(XYZ))) u0, v0 = np.ravel(UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(illuminant)))) W = 25 * Y**(1 / 3) - 17 U = 13 * W * (u - u0) V = 13 * W * (v - v0) return np.array([U, V, W])
def test_UCS_to_uv(self): """ Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition. """ np.testing.assert_almost_equal( UCS_to_uv(np.array([7.87055614, 10.34, 12.18252904])), (0.25895877609618834, 0.34020896328103534), decimal=7) np.testing.assert_almost_equal( UCS_to_uv(np.array([2.05793361, 3.2, 4.60117812])), (0.20873418076173886, 0.32457285074301517), decimal=7) np.testing.assert_almost_equal( UCS_to_uv(np.array([0.64604821, 1, 1.57635992])), (0.20048615319251942, 0.31032692311386395), decimal=7)
def test_UCS_to_uv(self): """ Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition. """ np.testing.assert_almost_equal( UCS_to_uv(np.array([0.04699689, 0.10080000, 0.16374390])), np.array([0.15085309, 0.32355314]), decimal=7) np.testing.assert_almost_equal( UCS_to_uv(np.array([0.31398473, 0.34950000, 0.34526969])), np.array([0.31125983, 0.34646688]), decimal=7) np.testing.assert_almost_equal( UCS_to_uv(np.array([0.17004543, 0.19150000, 0.20396469])), np.array([0.30069388, 0.33863231]), decimal=7)
def plot_spectral_locus(cmfs='CIE 1931 2 Degree Standard Observer', spectral_locus_colours=None, spectral_locus_labels=None, method='CIE 1931', **kwargs): """ Plots the *Spectral Locus* according to given method. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions defining the *Spectral Locus*. spectral_locus_colours : array_like or unicode, optional *Spectral Locus* colours, if ``spectral_locus_colours`` is set to *RGB*, the colours will be computed according to the corresponding chromaticity coordinates. spectral_locus_labels : array_like, optional Array of wavelength labels used to customise which labels will be drawn around the spectral locus. Passing an empty array will result in no wavelength labels being drawn. method : unicode, optional **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, *Chromaticity Diagram* method. 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. Examples -------- >>> plot_spectral_locus(spectral_locus_colours='RGB') # doctest: +SKIP .. image:: ../_static/Plotting_Plot_Spectral_Locus.png :align: center :alt: plot_spectral_locus """ if spectral_locus_colours is None: spectral_locus_colours = COLOUR_STYLE_CONSTANTS.colour.dark settings = {'uniform': True} settings.update(kwargs) figure, axes = artist(**settings) method = method.upper() cmfs = first_item(filter_cmfs(cmfs).values()) illuminant = COLOUR_STYLE_CONSTANTS.colour.colourspace.whitepoint wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) if method == 'CIE 1931': ij = XYZ_to_xy(cmfs.values, illuminant) labels = ((390, 460, 470, 480, 490, 500, 510, 520, 540, 560, 580, 600, 620, 700) if spectral_locus_labels is None else spectral_locus_labels) elif method == 'CIE 1960 UCS': ij = UCS_to_uv(XYZ_to_UCS(cmfs.values)) labels = ((420, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 645, 680) if spectral_locus_labels is None else spectral_locus_labels) elif method == 'CIE 1976 UCS': ij = Luv_to_uv(XYZ_to_Luv(cmfs.values, illuminant), illuminant) labels = ((420, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 645, 680) if spectral_locus_labels is None else spectral_locus_labels) else: raise ValueError( 'Invalid method: "{0}", must be one of ' '{\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\'}'.format( method)) pl_ij = tstack([ np.linspace(ij[0][0], ij[-1][0], 20), np.linspace(ij[0][1], ij[-1][1], 20) ]).reshape(-1, 1, 2) sl_ij = np.copy(ij).reshape(-1, 1, 2) if spectral_locus_colours.upper() == 'RGB': spectral_locus_colours = normalise_maximum( XYZ_to_plotting_colourspace(cmfs.values), axis=-1) if method == 'CIE 1931': XYZ = xy_to_XYZ(pl_ij) elif method == 'CIE 1960 UCS': XYZ = xy_to_XYZ(UCS_uv_to_xy(pl_ij)) elif method == 'CIE 1976 UCS': XYZ = xy_to_XYZ(Luv_uv_to_xy(pl_ij)) purple_line_colours = normalise_maximum( XYZ_to_plotting_colourspace(XYZ.reshape(-1, 3)), axis=-1) else: purple_line_colours = spectral_locus_colours for slp_ij, slp_colours in ((pl_ij, purple_line_colours), (sl_ij, spectral_locus_colours)): line_collection = LineCollection( np.concatenate([slp_ij[:-1], slp_ij[1:]], axis=1), colors=slp_colours) axes.add_collection(line_collection) wl_ij = dict(tuple(zip(wavelengths, ij))) for label in labels: i, j = wl_ij[label] index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] right = (wavelengths[index] if index < len(wavelengths) else wavelengths[-1]) dx = wl_ij[right][0] - wl_ij[left][0] dy = wl_ij[right][1] - wl_ij[left][1] ij = np.array([i, j]) direction = np.array([-dy, dx]) normal = (np.array([-dy, dx]) if np.dot( normalise_vector(ij - equal_energy), normalise_vector(direction)) > 0 else np.array([dy, -dx])) normal = normalise_vector(normal) / 30 label_colour = (spectral_locus_colours if is_string(spectral_locus_colours) else spectral_locus_colours[index]) axes.plot( (i, i + normal[0] * 0.75), (j, j + normal[1] * 0.75), color=label_colour) axes.plot(i, j, 'o', color=label_colour) axes.text( i + normal[0], j + normal[1], label, clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) settings = {'axes': axes} settings.update(kwargs) return render(**kwargs)
def plot_chromaticity_diagram_colours( samples=256, diagram_opacity=1.0, diagram_clipping_path=None, cmfs='CIE 1931 2 Degree Standard Observer', method='CIE 1931', **kwargs): """ Plots the *Chromaticity Diagram* colours according to given method. Parameters ---------- samples : numeric, optional Samples count on one axis. diagram_opacity : numeric, optional Opacity of the *Chromaticity Diagram* colours. diagram_clipping_path : array_like, optional Path of points used to clip the *Chromaticity Diagram* colours. cmfs : unicode, optional Standard observer colour matching functions used for *Chromaticity Diagram* bounds. method : unicode, optional **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, *Chromaticity Diagram* method. 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. Examples -------- >>> plot_chromaticity_diagram_colours() # doctest: +SKIP .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_Colours.png :align: center :alt: plot_chromaticity_diagram_colours """ settings = {'uniform': True} settings.update(kwargs) figure, axes = artist(**settings) method = method.upper() cmfs = first_item(filter_cmfs(cmfs).values()) illuminant = COLOUR_STYLE_CONSTANTS.colour.colourspace.whitepoint ii, jj = np.meshgrid( np.linspace(0, 1, samples), np.linspace(1, 0, samples)) ij = tstack([ii, jj]) if method == 'CIE 1931': XYZ = xy_to_XYZ(ij) spectral_locus = XYZ_to_xy(cmfs.values, illuminant) elif method == 'CIE 1960 UCS': XYZ = xy_to_XYZ(UCS_uv_to_xy(ij)) spectral_locus = UCS_to_uv(XYZ_to_UCS(cmfs.values)) elif method == 'CIE 1976 UCS': XYZ = xy_to_XYZ(Luv_uv_to_xy(ij)) spectral_locus = Luv_to_uv( XYZ_to_Luv(cmfs.values, illuminant), illuminant) else: raise ValueError( 'Invalid method: "{0}", must be one of ' '{\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\'}'.format( method)) RGB = normalise_maximum( XYZ_to_plotting_colourspace(XYZ, illuminant), axis=-1) polygon = Polygon( spectral_locus if diagram_clipping_path is None else diagram_clipping_path, facecolor='none', edgecolor='none') axes.add_patch(polygon) # Preventing bounding box related issues as per # https://github.com/matplotlib/matplotlib/issues/10529 image = axes.imshow( RGB, interpolation='bilinear', extent=(0, 1, 0, 1), clip_path=None, alpha=diagram_opacity) image.set_clip_path(polygon) settings = {'axes': axes} settings.update(kwargs) return render(**kwargs)
def planckian_locus_CIE_1960_UCS_chromaticity_diagram_plot( illuminants=None, **kwargs): """ Plots the planckian locus and given illuminants in *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- illuminants : array_like, optional Factory illuminants to plot. \**kwargs : dict, optional Keywords arguments. Returns ------- Figure Current figure or None. Raises ------ KeyError If one of the given illuminant is not found in the factory illuminants. Examples -------- >>> ils = ['A', 'C', 'E'] >>> planckian_locus_CIE_1960_UCS_chromaticity_diagram_plot( ... ils) # doctest: +SKIP """ if illuminants is None: illuminants = ('A', 'C', 'E') cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer') settings = { 'title': ('{0} Illuminants - Planckian Locus\n' 'CIE 1960 UCS Chromaticity Diagram - ' 'CIE 1931 2 Degree Standard Observer').format( ', '.join(illuminants)) if illuminants else ('Planckian Locus\nCIE 1960 UCS Chromaticity Diagram - ' 'CIE 1931 2 Degree Standard Observer'), 'standalone': False} settings.update(kwargs) CIE_1960_UCS_chromaticity_diagram_plot(**settings) start, end = 1667, 100000 uv = np.array([CCT_to_uv(x, 0, method='Robertson 1968') for x in np.arange(start, end + 250, 250)]) pylab.plot(uv[..., 0], uv[..., 1], color='black', linewidth=2) for i in (1667, 2000, 2500, 3000, 4000, 6000, 10000): u0, v0 = CCT_to_uv(i, -0.05, method='Robertson 1968') u1, v1 = CCT_to_uv(i, 0.05, method='Robertson 1968') pylab.plot((u0, u1), (v0, v1), color='black', linewidth=2) pylab.annotate('{0}K'.format(i), xy=(u0, v0), xytext=(0, -10), color='black', textcoords='offset points', size='x-small') for illuminant in illuminants: xy = ILLUMINANTS.get(cmfs.name).get(illuminant) if xy is None: raise KeyError( ('Illuminant "{0}" not found in factory illuminants: ' '"{1}".').format(illuminant, sorted(ILLUMINANTS.get(cmfs.name).keys()))) uv = UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy))) pylab.plot(uv[0], uv[1], 'o', color='white', linewidth=2) pylab.annotate(illuminant, xy=(uv[0], uv[1]), 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.2, 0.6), 'standalone': True}) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def colour_rendering_index(sd_test, additional_data=False): """ Returns the *Colour Rendering Index* (CRI) :math:`Q_a` of given spectral distribution. Parameters ---------- sd_test : SpectralDistribution Test spectral distribution. additional_data : bool, optional Whether to output additional data. Returns ------- numeric or CRI_Specification *Colour Rendering Index* (CRI). References ---------- :cite:`Ohno2008a` Examples -------- >>> from colour import ILLUMINANTS_SDS >>> sd = ILLUMINANTS_SDS['FL2'] >>> colour_rendering_index(sd) # doctest: +ELLIPSIS 64.1515202... """ cmfs = STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'].copy( ).trim(DEFAULT_SPECTRAL_SHAPE) shape = cmfs.shape sd_test = sd_test.copy().align(shape) tcs_sds = {sd.name: sd.copy().align(shape) for sd in TCS_SDS.values()} with domain_range_scale('1'): XYZ = sd_to_XYZ(sd_test, cmfs) uv = UCS_to_uv(XYZ_to_UCS(XYZ)) CCT, _D_uv = uv_to_CCT_Robertson1968(uv) if CCT < 5000: sd_reference = sd_blackbody(CCT, shape) else: xy = CCT_to_xy_CIE_D(CCT) sd_reference = sd_CIE_illuminant_D_series(xy) sd_reference.align(shape) test_tcs_colorimetry_data = tcs_colorimetry_data(sd_test, sd_reference, tcs_sds, cmfs, chromatic_adaptation=True) reference_tcs_colorimetry_data = tcs_colorimetry_data( sd_reference, sd_reference, tcs_sds, 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( sd_test.name, Q_a, Q_as, (test_tcs_colorimetry_data, reference_tcs_colorimetry_data)) else: return Q_a
def tcs_colorimetry_data(sd_t, sd_r, sds_tcs, cmfs, chromatic_adaptation=False): """ Returns the *test colour samples* colorimetry data. Parameters ---------- sd_t : SpectralDistribution Test spectral distribution. sd_r : SpectralDistribution Reference spectral distribution. sds_tcs : dict *Test colour samples* spectral 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 = sd_to_XYZ(sd_t, cmfs) uv_t = UCS_to_uv(XYZ_to_UCS(XYZ_t)) u_t, v_t = uv_t[0], uv_t[1] XYZ_r = sd_to_XYZ(sd_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()): sd_tcs = sds_tcs[value] XYZ_tcs = sd_to_XYZ(sd_tcs, cmfs, sd_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 * spow(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(sd_tcs.name, XYZ_tcs, uv_tcs, np.array([U_tcs, V_tcs, W_tcs]))) return tcs_data
def CIE_1960_UCS_chromaticity_diagram_plot( cmfs='CIE 1931 2 Degree Standard Observer', show_diagram_colours=True, **kwargs): """ Plots the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. show_diagram_colours : bool, optional Whether to display the chromaticity diagram background colours. Other Parameters ---------------- \**kwargs : dict, optional {:func:`boundaries`, :func:`canvas`, :func:`decorate`, :func:`display`}, Please refer to the documentation of the previously listed definitions. Returns ------- Figure Current figure or None. Examples -------- >>> CIE_1960_UCS_chromaticity_diagram_plot() # doctest: +SKIP """ settings = {'figure_size': (DEFAULT_FIGURE_WIDTH, DEFAULT_FIGURE_WIDTH)} settings.update(kwargs) canvas(**settings) cmfs = get_cmfs(cmfs) if show_diagram_colours: image = matplotlib.image.imread( os.path.join( PLOTTING_RESOURCES_DIRECTORY, 'CIE_1960_UCS_Chromaticity_Diagram_{0}.png'.format( cmfs.name.replace(' ', '_')))) pylab.imshow(image, interpolation=None, extent=(0, 1, 0, 1)) labels = (420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 640, 680) wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) uv = UCS_to_uv(XYZ_to_UCS(cmfs.values)) wavelengths_chromaticity_coordinates = dict(tuple(zip(wavelengths, uv))) pylab.plot(uv[..., 0], uv[..., 1], color='black', linewidth=2) pylab.plot((uv[-1][0], uv[0][0]), (uv[-1][1], uv[0][1]), color='black', linewidth=2) for label in labels: u, v = wavelengths_chromaticity_coordinates[label] pylab.plot(u, v, 'o', color='black', linewidth=2) index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] right = (wavelengths[index] if index < len(wavelengths) else wavelengths[-1]) dx = (wavelengths_chromaticity_coordinates[right][0] - wavelengths_chromaticity_coordinates[left][0]) dy = (wavelengths_chromaticity_coordinates[right][1] - wavelengths_chromaticity_coordinates[left][1]) uv = np.array([u, v]) direction = np.array([-dy, dx]) normal = (np.array([ -dy, dx ]) if np.dot(normalise_vector(uv - equal_energy), normalise_vector(direction)) > 0 else np.array([dy, -dx])) normal = normalise_vector(normal) normal /= 25 pylab.plot((u, u + normal[0] * 0.75), (v, v + normal[1] * 0.75), color='black', linewidth=1.5) pylab.text(u + normal[0], v + normal[1], label, color='black', clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) ticks = np.arange(-10, 10, 0.1) pylab.xticks(ticks) pylab.yticks(ticks) settings.update({ 'title': 'CIE 1960 UCS Chromaticity Diagram - {0}'.format(cmfs.title), 'x_label': 'CIE u', 'y_label': 'CIE v', 'grid': True, 'bounding_box': (0, 1, 0, 1) }) 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 XYZ_to_UVW( XYZ, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts from *CIE XYZ* tristimulus values to *CIE 1964 U\\*V\\*W\\** colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. illuminant : array_like, optional Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE 1964 U\\*V\\*W\\** colourspace array. Warning ------- The input domain and output range of that definition are non standard! Notes ----- +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``UVW`` | ``U`` : [-100, 100] | ``U`` : [-1, 1] | | | | | | | ``V`` : [-100, 100] | ``V`` : [-1, 1] | | | | | | | ``W`` : [0, 100] | ``W`` : [0, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`Wikipedia2008a` Examples -------- >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 >>> XYZ_to_UVW(XYZ) # doctest: +ELLIPSIS array([ 94.5503572..., 11.5553652..., 40.5475740...]) """ XYZ = to_domain_100(XYZ) xy = xyY_to_xy(illuminant) xyY = XYZ_to_xyY(XYZ, xy) _x, _y, Y = tsplit(xyY) u, v = tsplit(UCS_to_uv(XYZ_to_UCS(XYZ))) u_0, v_0 = tsplit(xy_to_UCS_uv(xy)) W = 25 * spow(Y, 1 / 3) - 17 U = 13 * W * (u - u_0) V = 13 * W * (v - v_0) UVW = tstack([U, V, W]) return from_range_100(UVW)
def CIE_1960_UCS_chromaticity_diagram_colours_plot( surface=1, samples=4096, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1960 UCS Chromaticity Diagram* colours. Parameters ---------- surface : numeric, optional Generated markers surface. samples : numeric, optional Samples count on one axis. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. Other Parameters ---------------- \**kwargs : dict, optional {:func:`boundaries`, :func:`canvas`, :func:`decorate`, :func:`display`}, Please refer to the documentation of the previously listed definitions. Returns ------- Figure Current figure or None. Examples -------- >>> CIE_1960_UCS_chromaticity_diagram_colours_plot() # doctest: +SKIP """ settings = {'figure_size': (64, 64)} settings.update(kwargs) canvas(**settings) cmfs = get_cmfs(cmfs) illuminant = DEFAULT_PLOTTING_ILLUMINANT triangulation = Delaunay(UCS_to_uv(XYZ_to_UCS(cmfs.values)), qhull_options='QJ') xx, yy = np.meshgrid(np.linspace(0, 1, samples), np.linspace(0, 1, samples)) xy = tstack((xx, yy)) xy = xy[triangulation.find_simplex(xy) > 0] XYZ = xy_to_XYZ(UCS_uv_to_xy(xy)) RGB = normalise_maximum(XYZ_to_sRGB(XYZ, illuminant), axis=-1) x_dot, y_dot = tsplit(xy) pylab.scatter(x_dot, y_dot, color=RGB, s=surface) settings.update({ 'x_ticker': False, 'y_ticker': False, 'bounding_box': (0, 1, 0, 1) }) settings.update(kwargs) ax = matplotlib.pyplot.gca() matplotlib.pyplot.setp(ax, frame_on=False) boundaries(**settings) decorate(**settings) return display(**settings)
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 spds_CIE_1960_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 1960 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_1960_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_1960_UCS_chromaticity_diagram_plot([A, D65]) # doctest: +SKIP """ settings = {} settings.update(kwargs) settings.update({'standalone': False}) CIE_1960_UCS_chromaticity_diagram_plot(cmfs=cmfs, **settings) for spd in spds: XYZ = spectral_to_XYZ(spd) / 100 uv = UCS_to_uv(XYZ_to_UCS(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.2, 0.6), '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 = 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