def excitation_purity(xy, xy_n, cmfs=CMFS['CIE 1931 2 Degree Standard Observer']): """ Returns the *excitation purity* :math:`P_e` for given colour stimulus :math:`xy`. Parameters ---------- xy : array_like Colour stimulus *xy* chromaticity coordinates. xy_n : array_like Achromatic stimulus *xy* chromaticity coordinates. cmfs : XYZ_ColourMatchingFunctions, optional Standard observer colour matching functions. Returns ------- numeric or array_like *Excitation purity* :math:`P_e`. Examples -------- >>> xy = np.array([0.28350, 0.68700]) >>> xy_n = np.array([0.31270, 0.32900]) >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer'] >>> excitation_purity(xy, xy_n, cmfs) # doctest: +ELLIPSIS 0.9386035... """ wl, xy_wl, xy_cwl = dominant_wavelength(xy, xy_n, cmfs) P_e = euclidean_distance(xy_n, xy) / euclidean_distance(xy_n, xy_wl) return P_e
def test_euclidean_distance(self): """ Tests :func:`colour.algebra.geometry.euclidean_distance` definition. """ self.assertAlmostEqual( euclidean_distance( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 426.67945353, 72.39590835])), 451.71330197, places=7) self.assertAlmostEqual( euclidean_distance( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 74.05216981, 276.45318193])), 52.64986116, places=7) self.assertAlmostEqual( euclidean_distance( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 8.32281957, -73.58297716])), 346.06489172, places=7)
def test_n_dimensional_euclidean_distance(self): """ Tests :func:`colour.algebra.geometry.euclidean_distance` definition n-dimensional arrays support. """ a = np.array([100.00000000, 21.57210357, 272.22819350]) b = np.array([100.00000000, 426.67945353, 72.39590835]) distance = 451.71330197 np.testing.assert_almost_equal( euclidean_distance(a, b), distance, decimal=7) a = np.tile(a, (6, 1)) b = np.tile(b, (6, 1)) distance = np.tile(distance, 6) np.testing.assert_almost_equal( euclidean_distance(a, b), distance, decimal=7) a = np.reshape(a, (2, 3, 3)) b = np.reshape(b, (2, 3, 3)) distance = np.reshape(distance, (2, 3)) np.testing.assert_almost_equal( euclidean_distance(a, b), distance, decimal=7)
def test_n_dimensional_euclidean_distance(self): """ Tests :func:`colour.algebra.geometry.euclidean_distance` definition n-dimensional arrays support. """ a = np.array([100.00000000, 21.57210357, 272.22819350]) b = np.array([100.00000000, 426.67945353, 72.39590835]) distance = 451.71330197 np.testing.assert_almost_equal(euclidean_distance(a, b), distance, decimal=7) a = np.tile(a, (6, 1)) b = np.tile(b, (6, 1)) distance = np.tile(distance, 6) np.testing.assert_almost_equal(euclidean_distance(a, b), distance, decimal=7) a = np.reshape(a, (2, 3, 3)) b = np.reshape(b, (2, 3, 3)) distance = np.reshape(distance, (2, 3)) np.testing.assert_almost_equal(euclidean_distance(a, b), distance, decimal=7)
def test_nan_euclidean_distance(self): """ Tests :func:`colour.algebra.geometry.euclidean_distance` 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: a = np.array(case) b = np.array(case) euclidean_distance(a, b)
def colour_quality_scales(test_data, reference_data, CCT_f): """ Returns the *VS test colour samples* rendering scales. Parameters ---------- test_data : list Test data. reference_data : list Reference data. CCT_f : numeric Factor penalizing lamps with extremely low correlated colour temperatures. Returns ------- dict *VS Test colour samples* colour rendering scales. """ Q_as = {} for i, _ in enumerate(test_data): D_C_ab = test_data[i].C - reference_data[i].C D_E_ab = euclidean_distance(test_data[i].Lab, reference_data[i].Lab) if D_C_ab > 0: D_Ep_ab = np.sqrt(D_E_ab**2 - D_C_ab**2) else: D_Ep_ab = D_E_ab Q_a = scale_conversion(D_Ep_ab, CCT_f) Q_as[i + 1] = VS_ColourQualityScaleData(test_data[i].name, Q_a, D_C_ab, D_E_ab, D_Ep_ab) return Q_as
def delta_E_CIE1976(Lab_1, Lab_2): """ Returns the difference :math:`\Delta E_{ab}` between two given *CIE L\*a\*b\** colourspace arrays using *CIE 1976* recommendation. Parameters ---------- Lab_1 : array_like *CIE L\*a\*b\** colourspace array 1. Lab_2 : array_like *CIE L\*a\*b\** colourspace array 2. Returns ------- numeric or ndarray Colour difference :math:`\Delta E_{ab}`. References ---------- - :cite:`Lindbloom2003c` Examples -------- >>> Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) >>> Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) >>> delta_E_CIE1976(Lab_1, Lab_2) # doctest: +ELLIPSIS 451.7133019... """ d_E = euclidean_distance(Lab_1, Lab_2) return d_E
def colour_rendering_indexes( test_data: Tuple[TCS_ColorimetryData, ...], reference_data: Tuple[TCS_ColorimetryData, ...], ) -> Dict[Integer, TCS_ColourQualityScaleData]: """ Return the *test colour samples* rendering indexes :math:`Q_a`. Parameters ---------- test_data Test data. reference_data Reference data. Returns ------- :class:`dict` *Test colour samples* *Colour Rendering Index* (CRI). """ Q_as = {} for i in range(len(test_data)): Q_as[i + 1] = TCS_ColourQualityScaleData( test_data[i].name, 100 - 4.6 * as_float_scalar( euclidean_distance(reference_data[i].UVW, test_data[i].UVW) ), ) return Q_as
def delta_E_DIN99(Lab_1, Lab_2, textiles=False): """ Returns the difference :math:`\\Delta E_{DIN99}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *DIN99* formula. Parameters ---------- Lab_1 : array_like *CIE L\\*a\\*b\\** colourspace array 1. Lab_2 : array_like *CIE L\\*a\\*b\\** colourspace array 2. Returns ------- numeric or ndarray Colour difference :math:`\\Delta E_{DIN99}`. Notes ----- +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ | ``Lab_1`` | ``L_1`` : [0, 100] | ``L_1`` : [0, 1] | | | | | | | ``a_1`` : [-100, 100] | ``a_1`` : [-1, 1] | | | | | | | ``b_1`` : [-100, 100] | ``b_1`` : [-1, 1] | +------------+-----------------------+-------------------+ | ``Lab_2`` | ``L_2`` : [0, 100] | ``L_2`` : [0, 1] | | | | | | | ``a_2`` : [-100, 100] | ``a_2`` : [-1, 1] | | | | | | | ``b_2`` : [-100, 100] | ``b_2`` : [-1, 1] | +------------+-----------------------+-------------------+ References ---------- :cite:`ASTMInternational2007` Examples -------- >>> import numpy as np >>> Lab_1 = np.array([60.2574, -34.0099, 36.2677]) >>> Lab_2 = np.array([60.4626, -34.1751, 39.4387]) >>> delta_E_DIN99(Lab_1, Lab_2) # doctest: +ELLIPSIS 1.1772166... """ k_E = 2 if textiles else 1 k_CH = 0.5 if textiles else 1 factor = 100 if get_domain_range_scale() == '1' else 1 d_E = euclidean_distance( Lab_to_DIN99(Lab_1, k_E, k_CH) * factor, Lab_to_DIN99(Lab_2, k_E, k_CH) * factor) return d_E
def excitation_purity( xy: ArrayLike, xy_n: ArrayLike, cmfs: Optional[MultiSpectralDistributions] = None, ) -> FloatingOrNDArray: """ Return the *excitation purity* :math:`P_e` for given colour stimulus :math:`xy`. Parameters ---------- xy Colour stimulus *CIE xy* chromaticity coordinates. xy_n Achromatic stimulus *CIE xy* chromaticity coordinates. cmfs Standard observer colour matching functions, default to the *CIE 1931 2 Degree Standard Observer*. Returns ------- :class:`np.floating` or :class:`numpy.ndarray` *Excitation purity* :math:`P_e`. References ---------- :cite:`CIETC1-482004o`, :cite:`Erdogana` Examples -------- >>> from colour.colorimetry import MSDS_CMFS >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer'] >>> xy = np.array([0.54369557, 0.32107944]) >>> xy_n = np.array([0.31270000, 0.32900000]) >>> excitation_purity(xy, xy_n, cmfs) # doctest: +ELLIPSIS 0.6228856... """ _wl, xy_wl, _xy_cwl = dominant_wavelength(xy, xy_n, cmfs) P_e = euclidean_distance(xy_n, xy) / euclidean_distance(xy_n, xy_wl) return P_e
def objective_function(M: ArrayLike, RGB: ArrayLike, Jab: ArrayLike) -> FloatingOrNDArray: """:math:`J_za_zb_z` colourspace based objective function.""" M = np.reshape(M, [3, 3]) XYZ_t = vector_dot(RGB_COLOURSPACE_ACES2065_1.matrix_RGB_to_XYZ, vector_dot(M, RGB)) Jab_t = XYZ_to_Jzazbz(XYZ_t) return np.sum(euclidean_distance(Jab, Jab_t))
def objective_function(M, RGB, Jab): """ :math:`J_zA_zB_z` colourspace based objective function. """ M = np.reshape(M, [3, 3]) XYZ_t = vector_dot(RGB_COLOURSPACE_ACES2065_1.matrix_RGB_to_XYZ, vector_dot(M, RGB)) Jab_t = XYZ_to_JzAzBz(XYZ_t) return np.sum(euclidean_distance(Jab, Jab_t))
def test_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition. """ Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) Lab_1 = np.tile(Lab_1, (6, 1)).reshape((2, 3, 3)) Lab_2 = np.tile(Lab_2, (6, 1)).reshape((2, 3, 3)) np.testing.assert_almost_equal(delta_E_CIE1976(Lab_1, Lab_2), euclidean_distance(Lab_1, Lab_2), decimal=7)
def excitation_purity(xy, xy_n, cmfs=CMFS['CIE 1931 2 Degree Standard Observer']): """ Returns the *excitation purity* :math:`P_e` for given colour stimulus :math:`xy`. Parameters ---------- xy : array_like Colour stimulus *CIE xy* chromaticity coordinates. xy_n : array_like Achromatic stimulus *CIE xy* chromaticity coordinates. cmfs : XYZ_ColourMatchingFunctions, optional Standard observer colour matching functions. Returns ------- numeric or array_like *Excitation purity* :math:`P_e`. References ---------- :cite:`CIETC1-482004o`, :cite:`Erdogana` Examples -------- >>> xy = np.array([0.54369557, 0.32107944]) >>> xy_n = np.array([0.31270000, 0.32900000]) >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer'] >>> excitation_purity(xy, xy_n, cmfs) # doctest: +ELLIPSIS 0.6228856... """ _wl, xy_wl, _xy_cwl = dominant_wavelength(xy, xy_n, cmfs) P_e = euclidean_distance(xy_n, xy) / euclidean_distance(xy_n, xy_wl) return P_e
def delta_E_CIE1976(Lab_1, Lab_2): """ Returns the difference :math:`\\Delta E_{76}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *CIE 1976* recommendation. Parameters ---------- Lab_1 : array_like *CIE L\\*a\\*b\\** colourspace array 1. Lab_2 : array_like *CIE L\\*a\\*b\\** colourspace array 2. Returns ------- numeric or ndarray Colour difference :math:`\\Delta E_{76}`. Notes ----- +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ | ``Lab_1`` | ``L_1`` : [0, 100] | ``L_1`` : [0, 1] | | | | | | | ``a_1`` : [-100, 100] | ``a_1`` : [-1, 1] | | | | | | | ``b_1`` : [-100, 100] | ``b_1`` : [-1, 1] | +------------+-----------------------+-------------------+ | ``Lab_2`` | ``L_2`` : [0, 100] | ``L_2`` : [0, 1] | | | | | | | ``a_2`` : [-100, 100] | ``a_2`` : [-1, 1] | | | | | | | ``b_2`` : [-100, 100] | ``b_2`` : [-1, 1] | +------------+-----------------------+-------------------+ References ---------- :cite:`Lindbloom2003c` Examples -------- >>> Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) >>> Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) >>> delta_E_CIE1976(Lab_1, Lab_2) # doctest: +ELLIPSIS 451.7133019... """ d_E = euclidean_distance(to_domain_100(Lab_1), to_domain_100(Lab_2)) return d_E
def test_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition. """ Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) Lab_1 = np.tile(Lab_1, (6, 1)).reshape([2, 3, 3]) Lab_2 = np.tile(Lab_2, (6, 1)).reshape([2, 3, 3]) np.testing.assert_almost_equal( delta_E_CIE1976(Lab_1, Lab_2), euclidean_distance(Lab_1, Lab_2), decimal=7)
def test_domain_range_scale_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition domain and range scale support. """ Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) d_r = (('reference', 1), (1, 0.01), (100, 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( delta_E_CIE1976(Lab_1 * factor, Lab_2 * factor), euclidean_distance(Lab_1, Lab_2), decimal=7)
def colour_quality_scales(test_data, reference_data, CCT_f): Q_as = {} from colour.algebra import euclidean_distance for i, _ in enumerate(test_data): D_C_ab = test_data[i].C - reference_data[i].C D_E_ab = euclidean_distance(test_data[i].Lab, reference_data[i].Lab) if D_C_ab > 0: D_Ep_ab = np.sqrt(D_E_ab**2 - D_C_ab**2) else: D_Ep_ab = D_E_ab Q_a = scale_conversion(D_Ep_ab, CCT_f) Q_as[i + 1] = VS_ColourQualityScaleData(test_data[i].name, Q_a, D_C_ab, D_E_ab, D_Ep_ab) return Q_as
def colour_quality_scales( test_data: Tuple[VS_ColorimetryData, ...], reference_data: Tuple[VS_ColorimetryData, ...], scaling_f: Floating, CCT_f: Floating, ) -> Dict[Integer, VS_ColourQualityScaleData]: """ Return the *VS test colour samples* rendering scales. Parameters ---------- test_data Test data. reference_data Reference data. scaling_f Scaling factor constant. CCT_f Factor penalizing lamps with extremely low correlated colour temperatures. Returns ------- :class:`dict` *VS Test colour samples* colour rendering scales. """ Q_as = {} for i in range(len(test_data)): D_C_ab = test_data[i].C - reference_data[i].C D_E_ab = as_float_scalar( euclidean_distance(test_data[i].Lab, reference_data[i].Lab)) if D_C_ab > 0: D_Ep_ab = np.sqrt(D_E_ab**2 - D_C_ab**2) else: D_Ep_ab = D_E_ab Q_a = scale_conversion(D_Ep_ab, CCT_f, scaling_f) Q_as[i + 1] = VS_ColourQualityScaleData(test_data[i].name, Q_a, D_C_ab, D_E_ab, D_Ep_ab) return Q_as
def colour_rendering_indexes(test_data, reference_data): """ Returns the *test colour samples* rendering indexes :math:`Q_a`. Parameters ---------- test_data : list Test data. reference_data : list Reference data. Returns ------- dict *Test colour samples* *Colour Rendering Index* (CRI). """ Q_as = {} for i, _ in enumerate(test_data): Q_as[i + 1] = TCS_ColourQualityScaleData( test_data[i].name, 100 - 4.6 * euclidean_distance(reference_data[i].UVW, test_data[i].UVW)) return Q_as
def delta_E_CIE1976(Lab_1, Lab_2): """ Returns the difference :math:`\Delta E_{ab}` between two given *CIE Lab* colourspace arrays using CIE 1976 recommendation. Parameters ---------- Lab_1 : array_like *CIE Lab* colourspace array 1. Lab_2 : array_like *CIE Lab* colourspace array 2. Returns ------- numeric or ndarray Colour difference :math:`\Delta E_{ab}`. See Also -------- colour.euclidean_distance References ---------- .. [2] Lindbloom, B. (2003). Delta E (CIE 1976). Retrieved February 24, 2014, from http://brucelindbloom.com/Eqn_DeltaE_CIE76.html Examples -------- >>> Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) >>> Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) >>> delta_E_CIE1976(Lab_1, Lab_2) # doctest: +ELLIPSIS 451.7133019... """ d_E = euclidean_distance(Lab_1, Lab_2) return d_E
def colour_quality_scales(test_data, reference_data, scaling_f, CCT_f): """ Returns the *VS test colour samples* rendering scales. Parameters ---------- test_data : list Test data. reference_data : list Reference data. scaling_f : numeric, optional Scaling factor constant. CCT_f : numeric Factor penalizing lamps with extremely low correlated colour temperatures. Returns ------- dict *VS Test colour samples* colour rendering scales. """ Q_as = {} for i, _ in enumerate(test_data): D_C_ab = test_data[i].C - reference_data[i].C D_E_ab = euclidean_distance(test_data[i].Lab, reference_data[i].Lab) if D_C_ab > 0: D_Ep_ab = np.sqrt(D_E_ab ** 2 - D_C_ab ** 2) else: D_Ep_ab = D_E_ab Q_a = scale_conversion(D_Ep_ab, scaling_f, CCT_f) Q_as[i + 1] = VS_ColourQualityScaleData(test_data[i].name, Q_a, D_C_ab, D_E_ab, D_Ep_ab) return Q_as
def delta_E_DIN99(Lab_1: ArrayLike, Lab_2: ArrayLike, textiles: Boolean = False) -> FloatingOrNDArray: """ Return the difference :math:`\\Delta E_{DIN99}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *DIN99* formula. Parameters ---------- Lab_1 *CIE L\\*a\\*b\\** colourspace array 1. Lab_2 *CIE L\\*a\\*b\\** colourspace array 2. textiles Textiles application specific parametric factors, :math:`k_E=2,\\ k_{CH}=0.5` weights are used instead of :math:`k_E=1,\\ k_{CH}=1`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E_{DIN99}`. Notes ----- +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ | ``Lab_1`` | ``L_1`` : [0, 100] | ``L_1`` : [0, 1] | | | | | | | ``a_1`` : [-100, 100] | ``a_1`` : [-1, 1] | | | | | | | ``b_1`` : [-100, 100] | ``b_1`` : [-1, 1] | +------------+-----------------------+-------------------+ | ``Lab_2`` | ``L_2`` : [0, 100] | ``L_2`` : [0, 1] | | | | | | | ``a_2`` : [-100, 100] | ``a_2`` : [-1, 1] | | | | | | | ``b_2`` : [-100, 100] | ``b_2`` : [-1, 1] | +------------+-----------------------+-------------------+ References ---------- :cite:`ASTMInternational2007` Examples -------- >>> import numpy as np >>> Lab_1 = np.array([60.2574, -34.0099, 36.2677]) >>> Lab_2 = np.array([60.4626, -34.1751, 39.4387]) >>> delta_E_DIN99(Lab_1, Lab_2) # doctest: +ELLIPSIS 1.1772166... """ k_E = 2 if textiles else 1 k_CH = 0.5 if textiles else 1 factor = 100 if get_domain_range_scale() == "1" else 1 d_E = euclidean_distance( Lab_to_DIN99(Lab_1, k_E, k_CH) * factor, Lab_to_DIN99(Lab_2, k_E, k_CH) * factor, ) return d_E
def colour_fidelity_index_CIE2017(sd_test, additional_data=False): """ Returns the *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f` of given spectral distribution. Parameters ---------- sd_test : SpectralDistribution Test spectral distribution. additional_data : bool, optional Whether to output additional data. Returns ------- numeric or ColourRendering_Specification_CIE2017 *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f`. References ---------- :cite:`CIETC1-902017` Examples -------- >>> from colour.colorimetry import SDS_ILLUMINANTS >>> sd = SDS_ILLUMINANTS['FL2'] >>> colour_fidelity_index_CIE2017(sd) # doctest: +ELLIPSIS 70.1208254... """ if sd_test.shape.start > 380 or sd_test.shape.end < 780: usage_warning('Test spectral distribution shape does not span the' 'recommended 380-780nm range, missing values will be' 'filled with zeros!') # NOTE: "CIE 2017 Colour Fidelity Index" standard recommends filling # missing values with zeros. sd_test = sd_test.copy() sd_test.extrapolator = Extrapolator sd_test.extrapolator_kwargs = { 'method': 'constant', 'left': 0, 'right': 0 } if sd_test.shape.interval > 5: raise ValueError('Test spectral distribution interval is greater than' '5nm which is the maximum recommended value ' 'for computing the "CIE 2017 Colour Fidelity Index"!') shape = SpectralShape(SPECTRAL_SHAPE_CIE2017.start, SPECTRAL_SHAPE_CIE2017.end, sd_test.shape.interval) CCT, D_uv = CCT_reference_illuminant(sd_test) sd_reference = sd_reference_illuminant(CCT, shape) # NOTE: All computations except CCT calculation use the # "CIE 1964 10 Degree Standard Observer". cmfs_10 = MSDS_CMFS['CIE 1964 10 Degree Standard Observer'].copy().align( shape) sds_tcs = load_TCS_CIE2017(shape).align(shape) test_tcs_colorimetry_data = tcs_colorimetry_data(sd_test, sds_tcs, cmfs_10) reference_tcs_colorimetry_data = tcs_colorimetry_data( sd_reference, sds_tcs, cmfs_10) delta_E_s = np.empty(len(sds_tcs.labels)) for i, _delta_E in enumerate(delta_E_s): delta_E_s[i] = euclidean_distance( test_tcs_colorimetry_data[i].Jpapbp, reference_tcs_colorimetry_data[i].Jpapbp) R_s = delta_E_to_R_f(delta_E_s) R_f = delta_E_to_R_f(np.average(delta_E_s)) if additional_data: return ColourRendering_Specification_CIE2017( sd_test.name, sd_reference, R_f, R_s, CCT, D_uv, (test_tcs_colorimetry_data, reference_tcs_colorimetry_data), delta_E_s) else: return R_f
def colour_fidelity_index_CIE2017( sd_test: SpectralDistribution, additional_data: Boolean = False ) -> Union[Floating, ColourRendering_Specification_CIE2017]: """ Return the *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f` of given spectral distribution. Parameters ---------- sd_test Test spectral distribution. additional_data Whether to output additional data. Returns ------- :class:`numpy.floating` or \ :class:`colour.quality.ColourRendering_Specification_CIE2017` *CIE 2017 Colour Fidelity Index* (CFI) :math:`R_f`. References ---------- :cite:`CIETC1-902017` Examples -------- >>> from colour.colorimetry import SDS_ILLUMINANTS >>> sd = SDS_ILLUMINANTS['FL2'] >>> colour_fidelity_index_CIE2017(sd) # doctest: +ELLIPSIS 70.1208254... """ if sd_test.shape.start > 380 or sd_test.shape.end < 780: usage_warning("Test spectral distribution shape does not span the" "recommended 380-780nm range, missing values will be" "filled with zeros!") # NOTE: "CIE 2017 Colour Fidelity Index" standard recommends filling # missing values with zeros. sd_test = cast(SpectralDistribution, sd_test.copy()) sd_test.extrapolator = Extrapolator sd_test.extrapolator_kwargs = { "method": "constant", "left": 0, "right": 0, } if sd_test.shape.interval > 5: raise ValueError("Test spectral distribution interval is greater than" "5nm which is the maximum recommended value " 'for computing the "CIE 2017 Colour Fidelity Index"!') shape = SpectralShape( SPECTRAL_SHAPE_CIE2017.start, SPECTRAL_SHAPE_CIE2017.end, sd_test.shape.interval, ) CCT, D_uv = tsplit(CCT_reference_illuminant(sd_test)) sd_reference = sd_reference_illuminant(CCT, shape) # NOTE: All computations except CCT calculation use the # "CIE 1964 10 Degree Standard Observer". # pylint: disable=E1102 cmfs_10 = reshape_msds(MSDS_CMFS["CIE 1964 10 Degree Standard Observer"], shape) # pylint: disable=E1102 sds_tcs = reshape_msds(load_TCS_CIE2017(shape), shape) test_tcs_colorimetry_data = tcs_colorimetry_data(sd_test, sds_tcs, cmfs_10) reference_tcs_colorimetry_data = tcs_colorimetry_data( sd_reference, sds_tcs, cmfs_10) delta_E_s = np.empty(len(sds_tcs.labels)) for i, _delta_E in enumerate(delta_E_s): delta_E_s[i] = euclidean_distance( test_tcs_colorimetry_data[i].Jpapbp, reference_tcs_colorimetry_data[i].Jpapbp, ) R_s = as_float_array(delta_E_to_R_f(delta_E_s)) R_f = as_float_scalar(delta_E_to_R_f(np.average(delta_E_s))) if additional_data: return ColourRendering_Specification_CIE2017( sd_test.name, sd_reference, R_f, R_s, CCT, D_uv, (test_tcs_colorimetry_data, reference_tcs_colorimetry_data), delta_E_s, ) else: return R_f