From bdbb4ba40a4f4a719dc01c2270c20124e18c7cff Mon Sep 17 00:00:00 2001 From: Tucker Downs Date: Tue, 25 Apr 2023 12:15:10 -0400 Subject: [PATCH] reshape basis functions, rather than align result every time more vectorization in TCS calls use correct order of arguments Remove CAM from spec struct --- colour/colorimetry/illuminants.py | 10 +++- colour/plotting/tm3018/components.py | 2 +- colour/quality/cfi2017.py | 70 +++++++++++++++++----------- colour/quality/tm3018.py | 2 +- 4 files changed, 53 insertions(+), 31 deletions(-) diff --git a/colour/colorimetry/illuminants.py b/colour/colorimetry/illuminants.py index 1371910109..45d2027e70 100644 --- a/colour/colorimetry/illuminants.py +++ b/colour/colorimetry/illuminants.py @@ -35,6 +35,7 @@ SpectralDistribution, SpectralShape, ) +from colour.colorimetry.spectrum import reshape_msds from colour.hints import ArrayLike, NDArrayFloat from colour.utilities import as_float_array, as_float, tsplit @@ -136,7 +137,9 @@ def sd_CIE_standard_illuminant_A( def sd_CIE_illuminant_D_series( - xy: ArrayLike, M1_M2_rounding: bool = True + xy: ArrayLike, + M1_M2_rounding: bool = True, + shape: SpectralShape | None = None, ) -> SpectralDistribution: """ Return the spectral distribution of given *CIE Illuminant D Series* using @@ -306,6 +309,11 @@ def sd_CIE_illuminant_D_series( S1 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S1"] S2 = SDS_BASIS_FUNCTIONS_CIE_ILLUMINANT_D_SERIES["S2"] + if shape is not None: + S0 = reshape_msds(S0, shape=shape, copy=False) + S1 = reshape_msds(S1, shape=shape, copy=False) + S2 = reshape_msds(S2, shape=shape, copy=False) + distribution = S0.values + M1 * S1.values + M2 * S2.values return SpectralDistribution( diff --git a/colour/plotting/tm3018/components.py b/colour/plotting/tm3018/components.py index 8327a0cbf9..02f8bb3201 100644 --- a/colour/plotting/tm3018/components.py +++ b/colour/plotting/tm3018/components.py @@ -390,7 +390,7 @@ def plot_colour_vector_graphic( [ np.mean( [ - cast(float, specification.colorimetry_data[1][i].CAM.h) + cast(float, specification.colorimetry_data[1][i].JMh[2]) for i in specification.bins[j] ] ) diff --git a/colour/quality/cfi2017.py b/colour/quality/cfi2017.py index d1c3e0f5dc..e2dbed57a8 100644 --- a/colour/quality/cfi2017.py +++ b/colour/quality/cfi2017.py @@ -21,7 +21,6 @@ from colour.algebra import Extrapolator, euclidean_distance, linstep_function from colour.appearance import ( - CAM_Specification_CIECAM02, XYZ_to_CIECAM02, VIEWING_CONDITIONS_CIECAM02, ) @@ -93,7 +92,6 @@ class DataColorimetry_TCS_CIE2017: name: str XYZ: NDArrayFloat - CAM: CAM_Specification_CIECAM02 JMh: NDArrayFloat Jpapbp: NDArrayFloat @@ -212,10 +210,10 @@ def colour_fidelity_index_CIE2017( # pylint: disable=E1102 sds_tcs = load_TCS_CIE2017(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 - ) + ( + test_tcs_colorimetry_data, + reference_tcs_colorimetry_data, + ) = tcs_colorimetry_data([sd_test, sd_reference], sds_tcs, cmfs_10) delta_E_s = euclidean_distance( [test_sample.Jpapbp for test_sample in test_tcs_colorimetry_data], @@ -381,7 +379,7 @@ def sd_reference_illuminant( if CCT >= 4000: xy = CCT_to_xy_CIE_D(CCT) - sd_daylight = sd_CIE_illuminant_D_series(xy).align(shape) + sd_daylight = sd_CIE_illuminant_D_series(xy, shape=shape) if CCT < 4000: sd_reference = sd_planckian @@ -413,7 +411,7 @@ def sd_reference_illuminant( def tcs_colorimetry_data( - sd_irradiance: SpectralDistribution, + sd_irradiance: SpectralDistribution | list[SpectralDistribution], sds_tcs: MultiSpectralDistributions, cmfs: MultiSpectralDistributions, ) -> Tuple[DataColorimetry_TCS_CIE2017, ...]: @@ -443,29 +441,40 @@ def tcs_colorimetry_data( >>> delta_E_to_R_f(4.4410383190) # doctest: +ELLIPSIS 70.1208254... """ + if type(sd_irradiance) is SpectralDistribution: + sd_irradiance = [sd_irradiance] + + XYZ_w = np.full((len(sd_irradiance), 3), np.nan) + for idx, sd in enumerate(sd_irradiance): + XYZ_t = sd_to_XYZ( + sd.values, + cmfs, + shape=sd.shape, + method="Integration", + ) + k = 100 / XYZ_t[1] + XYZ_w[idx] = k * XYZ_t + sd_irradiance[idx] *= k + XYZ_w = as_float_array(XYZ_w) - XYZ_w = sd_to_XYZ( - sd_irradiance.values, - cmfs, - shape=sd_irradiance.shape, - method="Integration", - ) - XYZ_w *= 100 / XYZ_w[1] Y_b = 20 L_A = 100 surround = VIEWING_CONDITIONS_CIECAM02["Average"] + sds_tcs_t = np.tile(sds_tcs.values.T, (len(sd_irradiance), 1, 1)) + sds_tcs_t = sds_tcs_t * as_float_array( + [sd.values for sd in sd_irradiance] + ).reshape(len(sd_irradiance), 1, len(sd_irradiance[0])) + XYZ = msds_to_XYZ( - sds_tcs.values.T, + sds_tcs_t, cmfs, - sd_irradiance, - use_practice_range=False, method="Integration", shape=sds_tcs.shape, ) specification = XYZ_to_CIECAM02( XYZ, - XYZ_w, + XYZ_w.reshape((len(sd_irradiance), 1, 3)), L_A, Y_b, surround, @@ -481,16 +490,21 @@ def tcs_colorimetry_data( ) Jpapbp = JMh_CIECAM02_to_CAM02UCS(JMh) specification = as_float_array(specification) - tcs_data = [ - DataColorimetry_TCS_CIE2017( - sds_tcs.display_labels[idx], # @tjdcs Performance Hack - XYZ[idx], - CAM_Specification_CIECAM02(*specification[idx]), - JMh[idx], - Jpapbp[idx], + tcs_data = [] + # fmt: off + for sd_idx in range(len(sd_irradiance)): + tcs_data.append( + [ + DataColorimetry_TCS_CIE2017( + sds_tcs.display_labels[idx], # @tjdcs Performance Hack + XYZ[sd_idx][idx], + JMh[sd_idx][idx], + Jpapbp[sd_idx][idx], + ) + for idx in range(len(sds_tcs.signals)) + ] ) - for idx in range(len(sds_tcs.signals)) - ] + #fmt: on return tuple(tcs_data) diff --git a/colour/quality/tm3018.py b/colour/quality/tm3018.py index 1c19e790d6..d473fa4914 100644 --- a/colour/quality/tm3018.py +++ b/colour/quality/tm3018.py @@ -143,7 +143,7 @@ def colour_fidelity_index_ANSIIESTM3018( # Setup bins based on where the reference a'b' points are located. bins = np.floor( as_float_array( - [sample.CAM.h for sample in specification.colorimetry_data[1]] + [sample.JMh[2] for sample in specification.colorimetry_data[1]] ) / 22.5 )