def test_n_dimensional_CCT_to_xy_CIE_D(self): """ Tests :func:`colour.temperature.cct.CCT_to_xy_CIE_D` definition n-dimensional arrays support. """ CCT = 4000 xy = np.array([0.382343625000000, 0.383766261015578]) np.testing.assert_almost_equal( CCT_to_xy_CIE_D(CCT), xy, decimal=7) CCT = np.tile(CCT, 6) xy = np.tile(xy, (6, 1)) np.testing.assert_almost_equal( CCT_to_xy_CIE_D(CCT), xy, decimal=7) CCT = np.reshape(CCT, (2, 3)) xy = np.reshape(xy, (2, 3, 2)) np.testing.assert_almost_equal( CCT_to_xy_CIE_D(CCT), xy, decimal=7)
def test_n_dimensional_CCT_to_xy_CIE_D(self): """ Test :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition n-dimensional arrays support. """ CCT = 4000 xy = CCT_to_xy_CIE_D(CCT) CCT = np.tile(CCT, 6) xy = np.tile(xy, (6, 1)) np.testing.assert_almost_equal(CCT_to_xy_CIE_D(CCT), xy, decimal=7) CCT = np.reshape(CCT, (2, 3)) xy = np.reshape(xy, (2, 3, 2)) np.testing.assert_almost_equal(CCT_to_xy_CIE_D(CCT), xy, decimal=7)
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 test_CCT_to_xy_CIE_D(self): """ Tests :func:`colour.temperature.cie_d.CCT_to_xy_CIE_D` definition. """ np.testing.assert_almost_equal( CCT_to_xy_CIE_D(4000), np.array([0.382343625000000, 0.383766261015578]), decimal=7) np.testing.assert_almost_equal( CCT_to_xy_CIE_D(7000), np.array([0.305357431486880, 0.321646345474552]), decimal=7) np.testing.assert_almost_equal(CCT_to_xy_CIE_D(25000), np.array( [0.24985367, 0.254799464210944]), decimal=7)
def test_sd_CIE_illuminant_D_series(self): """ Tests :func:`colour.colorimetry.illuminants.\ sd_CIE_illuminant_D_series` definition. """ for name, CCT, tolerance in ( ('D50', 5000, 0.001), ('D55', 5500, 0.001), ('D65', 6500, 0.00001), ('D75', 7500, 0.0001), ): CCT = CCT * 1.4388 / 1.4380 xy = CCT_to_xy_CIE_D(CCT) sd_r = ILLUMINANTS_SDS[name] sd_t = sd_CIE_illuminant_D_series(xy) np.testing.assert_allclose(sd_r.values, sd_t[sd_r.wavelengths], rtol=tolerance, atol=tolerance)
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 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 colour_quality_scale(sd_test, additional_data=False, method='NIST CQS 9.0'): """ Returns the *Colour Quality Scale* (CQS) of given spectral distribution using given method. Parameters ---------- sd_test : SpectralDistribution Test spectral distribution. additional_data : bool, optional Whether to output additional data. method : unicode, optional **{'NIST CQS 9.0', 'NIST CQS 7.4'}**, Computation method. Returns ------- numeric or CQS_Specification Color quality scale. References ---------- :cite:`Davis2010a`, :cite:`Ohno2008a`, :cite:`Ohno2013` Examples -------- >>> from colour import ILLUMINANTS_SDS >>> sd = ILLUMINANTS_SDS['FL2'] >>> colour_quality_scale(sd) # doctest: +ELLIPSIS 64.0172835... """ method = method.lower() assert method.lower() in [ m.lower() for m in COLOUR_QUALITY_SCALE_METHODS ], ('"{0}" method is invalid, must be one of {1}!'.format( method, COLOUR_QUALITY_SCALE_METHODS)) 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) vs_sds = { sd.name: sd.copy().align(shape) for sd in VS_SDS[method].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_Ohno2013(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_vs_colorimetry_data = vs_colorimetry_data(sd_test, sd_reference, vs_sds, cmfs, chromatic_adaptation=True) reference_vs_colorimetry_data = vs_colorimetry_data( sd_reference, sd_reference, vs_sds, cmfs) if method == 'nist cqs 9.0': CCT_f = 1 scaling_f = 3.2 else: XYZ_r = sd_to_XYZ(sd_reference, cmfs) XYZ_r /= XYZ_r[1] CCT_f = CCT_factor(reference_vs_colorimetry_data, XYZ_r) scaling_f = 3.104 Q_as = colour_quality_scales(test_vs_colorimetry_data, reference_vs_colorimetry_data, scaling_f, 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, scaling_f) if method == 'nist cqs 9.0': scaling_f = 2.93 * 1.0343 else: scaling_f = 2.928 Q_f = scale_conversion(D_E_RMS, CCT_f, scaling_f) 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 if method == 'nist cqs 9.0': Q_d = Q_p = None else: 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) Q_d = G_t / G_r * CCT_f * 100 if additional_data: return CQS_Specification( sd_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 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 make_xy_value_from_temperature(temperature=6500): return CCT_to_xy_CIE_D(temperature * 1.4388 / 1.4380)
def sd_reference_illuminant(CCT: Floating, shape: SpectralShape) -> SpectralDistribution: """ Compute the reference illuminant for a given correlated colour temperature :math:`T_{cp}` for use in *CIE 2017 Colour Fidelity Index* (CFI) computation. Parameters ---------- CCT Correlated colour temperature :math:`T_{cp}`. shape Desired shape of the returned spectral distribution. Returns ------- :class:`colour.SpectralDistribution` Reference illuminant for *CIE 2017 Colour Fidelity Index* (CFI) computation. Examples -------- >>> from colour.utilities import numpy_print_options >>> with numpy_print_options(suppress=True): ... sd_reference_illuminant( # doctest: +ELLIPSIS ... 4224.469705295263300, SpectralShape(380, 780, 20)) SpectralDistribution([[ 380. , 0.0034089...], [ 400. , 0.0044208...], [ 420. , 0.0053260...], [ 440. , 0.0062857...], [ 460. , 0.0072767...], [ 480. , 0.0080207...], [ 500. , 0.0086590...], [ 520. , 0.0092242...], [ 540. , 0.0097686...], [ 560. , 0.0101444...], [ 580. , 0.0104475...], [ 600. , 0.0107642...], [ 620. , 0.0110439...], [ 640. , 0.0112535...], [ 660. , 0.0113922...], [ 680. , 0.0115185...], [ 700. , 0.0113155...], [ 720. , 0.0108192...], [ 740. , 0.0111582...], [ 760. , 0.0101299...], [ 780. , 0.0105638...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...}) """ if CCT <= 5000: sd_planckian = sd_blackbody(CCT, shape) if CCT >= 4000: xy = CCT_to_xy_CIE_D(CCT) sd_daylight = sd_CIE_illuminant_D_series(xy).align(shape) if CCT < 4000: sd_reference = sd_planckian elif 4000 <= CCT <= 5000: # Planckian and daylight illuminant must be normalised so that the # mixture isn't biased. sd_planckian /= sd_to_XYZ(sd_planckian)[1] # type: ignore[misc] sd_daylight /= sd_to_XYZ(sd_daylight)[1] # type: ignore[misc] # Mixture: 4200K should be 80% Planckian, 20% CIE Illuminant D Series. m = (CCT - 4000) / 1000 values = linstep_function(m, sd_planckian.values, sd_daylight.values) name = (f"{as_int_scalar(CCT)}K " f"Blackbody & CIE Illuminant D Series Mixture - " f"{as_float_scalar(100 * m):.1f}%") sd_reference = SpectralDistribution(values, shape.range(), name=name) elif CCT > 5000: sd_reference = sd_daylight return sd_reference
def colour_rendering_index(spd_test,T, 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.1495478... """ cmfs = STANDARD_OBSERVERS_CMFS.get('CIE 1931 2 Degree Standard Observer') shape = cmfs.shape CCT, _D_uv = T[0],T[1] 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 generate_illuminants_rawtoaces_v1() -> CaseInsensitiveMapping: """ Generate a series of illuminants according to *RAW to ACES* v1: - *CIE Illuminant D Series* in range [4000, 25000] kelvin degrees. - *Blackbodies* in range [1000, 3500] kelvin degrees. - A.M.P.A.S. variant of *ISO 7589 Studio Tungsten*. Returns ------- :class:`colour.utilities.CaseInsensitiveMapping` Series of illuminants. Notes ----- - This definition introduces a few differences compared to *RAW to ACES* v1: *CIE Illuminant D Series* are computed in range [4002.15, 7003.77] kelvin degrees and the :math:`C_2` change is not used in *RAW to ACES* v1. References ---------- :cite:`Dyer2017` Examples -------- >>> list(sorted(generate_illuminants_rawtoaces_v1().keys())) ['1000K Blackbody', '1500K Blackbody', '2000K Blackbody', \ '2500K Blackbody', '3000K Blackbody', '3500K Blackbody', 'D100', 'D105', \ 'D110', 'D115', 'D120', 'D125', 'D130', 'D135', 'D140', 'D145', 'D150', \ 'D155', 'D160', 'D165', 'D170', 'D175', 'D180', 'D185', 'D190', 'D195', \ 'D200', 'D205', 'D210', 'D215', 'D220', 'D225', 'D230', 'D235', 'D240', \ 'D245', 'D250', 'D40', 'D45', 'D50', 'D55', 'D60', 'D65', 'D70', 'D75', \ 'D80', 'D85', 'D90', 'D95', 'iso7589'] """ global _ILLUMINANTS_RAWTOACES_V1 if _ILLUMINANTS_RAWTOACES_V1 is not None: illuminants = _ILLUMINANTS_RAWTOACES_V1 else: illuminants = CaseInsensitiveMapping() # CIE Illuminants D Series from 4000K to 25000K. for i in np.arange(4000, 25000 + 500, 500): CCT = i * 1.4388 / 1.4380 xy = CCT_to_xy_CIE_D(CCT) sd = sd_CIE_illuminant_D_series(xy) sd.name = f"D{int(CCT / 100):d}" illuminants[sd.name] = sd.align(SPECTRAL_SHAPE_RAWTOACES) # TODO: Remove when removing the "colour.sd_blackbody" definition # warning. with suppress_warnings(colour_usage_warnings=True): # Blackbody from 1000K to 4000K. for i in np.arange(1000, 4000, 500): sd = sd_blackbody(i, SPECTRAL_SHAPE_RAWTOACES) illuminants[sd.name] = sd # A.M.P.A.S. variant of ISO 7589 Studio Tungsten. sd = read_sds_from_csv_file( os.path.join(RESOURCES_DIRECTORY_RAWTOACES, "AMPAS_ISO_7589_Tungsten.csv"))["iso7589"] illuminants.update({sd.name: sd}) _ILLUMINANTS_RAWTOACES_V1 = illuminants return illuminants
def colour_rendering_index( sd_test: SpectralDistribution, additional_data: Boolean = False ) -> Union[Floating, ColourRendering_Specification_CRI]: """ Return the *Colour Rendering Index* (CRI) :math:`Q_a` 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_CRI` *Colour Rendering Index* (CRI). References ---------- :cite:`Ohno2008a` Examples -------- >>> from colour import SDS_ILLUMINANTS >>> sd = SDS_ILLUMINANTS['FL2'] >>> colour_rendering_index(sd) # doctest: +ELLIPSIS 64.2337241... """ # pylint: disable=E1102 cmfs = reshape_msds( MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], SPECTRAL_SHAPE_DEFAULT, ) shape = cmfs.shape sd_test = reshape_sd(sd_test, shape) tcs_sds = {sd.name: reshape_sd(sd, shape) for sd in SDS_TCS.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 = as_float_scalar( 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 ColourRendering_Specification_CRI( sd_test.name, Q_a, Q_as, (test_tcs_colorimetry_data, reference_tcs_colorimetry_data), ) else: return Q_a
def colour_quality_scale( sd_test: SpectralDistribution, additional_data: Boolean = False, method: Union[Literal["NIST CQS 7.4", "NIST CQS 9.0"], str] = "NIST CQS 9.0", ) -> Union[Floating, ColourRendering_Specification_CQS]: """ Return the *Colour Quality Scale* (CQS) of given spectral distribution using given method. Parameters ---------- sd_test Test spectral distribution. additional_data Whether to output additional data. method Computation method. Returns ------- :class:`numpy.floating` or \ :class:`colour.quality.ColourRendering_Specification_CQS` *Colour Quality Scale* (CQS). References ---------- :cite:`Davis2010a`, :cite:`Ohno2008a`, :cite:`Ohno2013` Examples -------- >>> from colour import SDS_ILLUMINANTS >>> sd = SDS_ILLUMINANTS['FL2'] >>> colour_quality_scale(sd) # doctest: +ELLIPSIS 64.1117031... """ method = validate_method(method, COLOUR_QUALITY_SCALE_METHODS) # pylint: disable=E1102 cmfs = reshape_msds( MSDS_CMFS["CIE 1931 2 Degree Standard Observer"], SPECTRAL_SHAPE_DEFAULT, ) shape = cmfs.shape sd_test = reshape_sd(sd_test, shape) vs_sds = {sd.name: reshape_sd(sd, shape) for sd in SDS_VS[method].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_Ohno2013(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_vs_colorimetry_data = vs_colorimetry_data(sd_test, sd_reference, vs_sds, cmfs, chromatic_adaptation=True) reference_vs_colorimetry_data = vs_colorimetry_data( sd_reference, sd_reference, vs_sds, cmfs) CCT_f: Floating if method == "nist cqs 9.0": CCT_f = 1 scaling_f = 3.2 else: XYZ_r = sd_to_XYZ(sd_reference, cmfs) XYZ_r /= XYZ_r[1] CCT_f = CCT_factor(reference_vs_colorimetry_data, XYZ_r) scaling_f = 3.104 Q_as = colour_quality_scales( test_vs_colorimetry_data, reference_vs_colorimetry_data, scaling_f, 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, scaling_f) if method == "nist cqs 9.0": scaling_f = 2.93 * 1.0343 else: scaling_f = 2.928 Q_f = scale_conversion(D_E_RMS, CCT_f, scaling_f) 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 / GAMUT_AREA_D65 * 100 if method == "nist cqs 9.0": Q_p = Q_d = None else: 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 = as_float_scalar(100 - 3.6 * (D_Ep_RMS - p_delta_C)) Q_d = as_float_scalar(G_t / G_r * CCT_f * 100) if additional_data: return ColourRendering_Specification_CQS( sd_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