def test_row_as_diagonal(self): """ Tests :func:`colour.utilities.array.row_as_diagonal` definition. """ np.testing.assert_almost_equal( row_as_diagonal(np.array([[0.25891593, 0.07299478, 0.36586996], [0.30851087, 0.37131459, 0.16274825], [0.71061831, 0.67718718, 0.09562581], [0.71588836, 0.76772047, 0.15476079], [0.92985142, 0.22263399, 0.88027331]])), np.array([[[0.25891593, 0.00000000, 0.00000000], [0.00000000, 0.07299478, 0.00000000], [0.00000000, 0.00000000, 0.36586996]], [[0.30851087, 0.00000000, 0.00000000], [0.00000000, 0.37131459, 0.00000000], [0.00000000, 0.00000000, 0.16274825]], [[0.71061831, 0.00000000, 0.00000000], [0.00000000, 0.67718718, 0.00000000], [0.00000000, 0.00000000, 0.09562581]], [[0.71588836, 0.00000000, 0.00000000], [0.00000000, 0.76772047, 0.00000000], [0.00000000, 0.00000000, 0.15476079]], [[0.92985142, 0.00000000, 0.00000000], [0.00000000, 0.22263399, 0.00000000], [0.00000000, 0.00000000, 0.88027331]]]))
def test_row_as_diagonal(self): """ Tests :func:`colour.utilities.array.row_as_diagonal` definition. """ np.testing.assert_almost_equal( row_as_diagonal(np.array( [[0.25891593, 0.07299478, 0.36586996], [0.30851087, 0.37131459, 0.16274825], [0.71061831, 0.67718718, 0.09562581], [0.71588836, 0.76772047, 0.15476079], [0.92985142, 0.22263399, 0.88027331]]) ), np.array( [[[0.25891593, 0.00000000, 0.00000000], [0.00000000, 0.07299478, 0.00000000], [0.00000000, 0.00000000, 0.36586996]], [[0.30851087, 0.00000000, 0.00000000], [0.00000000, 0.37131459, 0.00000000], [0.00000000, 0.00000000, 0.16274825]], [[0.71061831, 0.00000000, 0.00000000], [0.00000000, 0.67718718, 0.00000000], [0.00000000, 0.00000000, 0.09562581]], [[0.71588836, 0.00000000, 0.00000000], [0.00000000, 0.76772047, 0.00000000], [0.00000000, 0.00000000, 0.15476079]], [[0.92985142, 0.00000000, 0.00000000], [0.00000000, 0.22263399, 0.00000000], [0.00000000, 0.00000000, 0.88027331]]] ) ) # yapf: disable
def chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform='CAT02'): M = CHROMATIC_ADAPTATION_TRANSFORMS.get(transform) print('XYZ_w', XYZ_w) print('XYZ_wr', XYZ_wr) if M is None: raise KeyError( '"{0}" chromatic adaptation transform is not defined! Supported ' 'methods: "{1}".'.format(transform, CHROMATIC_ADAPTATION_TRANSFORMS.keys())) print('M', M) print('M inv', np.linalg.inv(M)) rgb_w = np.einsum('...i,...ij->...j', XYZ_w, np.transpose(M)) rgb_wr = np.einsum('...i,...ij->...j', XYZ_wr, np.transpose(M)) print('rgb_w: ', rgb_w) print('rgb_wr: ', rgb_wr) D = rgb_wr / rgb_w print('D: ', D) D = row_as_diagonal(D) M_CAT = dot_matrix(np.linalg.inv(M), D) M_CAT = dot_matrix(M_CAT, M) return M_CAT
def chromatic_adaptation_Fairchild1990( XYZ_1: ArrayLike, XYZ_n: ArrayLike, XYZ_r: ArrayLike, Y_n: FloatingOrArrayLike, discount_illuminant: Boolean = False, ) -> NDArray: """ Adapt given stimulus *CIE XYZ_1* tristimulus values from test viewing conditions to reference viewing conditions using *Fairchild (1990)* chromatic adaptation model. Parameters ---------- XYZ_1 *CIE XYZ_1* tristimulus values of test sample / stimulus. XYZ_n Test viewing condition *CIE XYZ_n* tristimulus values of whitepoint. XYZ_r Reference viewing condition *CIE XYZ_r* tristimulus values of whitepoint. Y_n Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. discount_illuminant Truth value indicating if the illuminant should be discounted. Returns ------- :class:`numpy.ndarray` Adapted *CIE XYZ_2* tristimulus values of stimulus. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_1`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_n`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_r`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_2`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Fairchild1991a`, :cite:`Fairchild2013s` Examples -------- >>> XYZ_1 = np.array([19.53, 23.07, 24.97]) >>> XYZ_n = np.array([111.15, 100.00, 35.20]) >>> XYZ_r = np.array([94.81, 100.00, 107.30]) >>> Y_n = 200 >>> chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n) ... # doctest: +ELLIPSIS array([ 23.3252634..., 23.3245581..., 76.1159375...]) """ XYZ_1 = to_domain_100(XYZ_1) XYZ_n = to_domain_100(XYZ_n) XYZ_r = to_domain_100(XYZ_r) Y_n = as_float_array(Y_n) LMS_1 = vector_dot(MATRIX_XYZ_TO_RGB_FAIRCHILD1990, XYZ_1) LMS_n = vector_dot(MATRIX_XYZ_TO_RGB_FAIRCHILD1990, XYZ_n) LMS_r = vector_dot(MATRIX_XYZ_TO_RGB_FAIRCHILD1990, XYZ_r) p_LMS = degrees_of_adaptation( LMS_1, Y_n, discount_illuminant=discount_illuminant ) a_LMS_1 = p_LMS / LMS_n a_LMS_2 = p_LMS / LMS_r A_1 = row_as_diagonal(a_LMS_1) A_2 = row_as_diagonal(a_LMS_2) LMSp_1 = vector_dot(A_1, LMS_1) c = 0.219 - 0.0784 * np.log10(Y_n) C = row_as_diagonal(tstack([c, c, c])) LMS_a = vector_dot(C, LMSp_1) LMSp_2 = vector_dot(np.linalg.inv(C), LMS_a) LMS_c = vector_dot(np.linalg.inv(A_2), LMSp_2) XYZ_c = vector_dot(MATRIX_RGB_TO_XYZ_FAIRCHILD1990, LMS_c) return from_range_100(XYZ_c)
def chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform='CAT02'): """ Computes the *chromatic adaptation* matrix from test viewing conditions to reference viewing conditions. Parameters ---------- XYZ_w : array_like Test viewing condition *CIE XYZ* tristimulus values of whitepoint. XYZ_wr : array_like Reference viewing condition *CIE XYZ* tristimulus values of whitepoint. transform : unicode, optional **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco', 'Bianco PC'}**, Chromatic adaptation transform. Returns ------- ndarray Chromatic adaptation matrix :math:`M_{cat}`. Raises ------ KeyError If chromatic adaptation method is not defined. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_w`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wr`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Fairchild2013t` Examples -------- >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr) ... # doctest: +ELLIPSIS array([[ 1.0425738..., 0.0308910..., -0.0528125...], [ 0.0221934..., 1.0018566..., -0.0210737...], [-0.0011648..., -0.0034205..., 0.7617890...]]) Using Bradford method: >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> method = 'Bradford' >>> chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, method) ... # doctest: +ELLIPSIS array([[ 1.0479297..., 0.0229468..., -0.0501922...], [ 0.0296278..., 0.9904344..., -0.0170738...], [-0.0092430..., 0.0150551..., 0.7518742...]]) """ XYZ_w = to_domain_1(XYZ_w) XYZ_wr = to_domain_1(XYZ_wr) M = CHROMATIC_ADAPTATION_TRANSFORMS.get(transform) if M is None: raise KeyError( '"{0}" chromatic adaptation transform is not defined! Supported ' 'methods: "{1}".'.format(transform, CHROMATIC_ADAPTATION_TRANSFORMS.keys())) rgb_w = np.einsum('...i,...ij->...j', XYZ_w, np.transpose(M)) rgb_wr = np.einsum('...i,...ij->...j', XYZ_wr, np.transpose(M)) D = rgb_wr / rgb_w D = row_as_diagonal(D) M_CAT = dot_matrix(np.linalg.inv(M), D) M_CAT = dot_matrix(M_CAT, M) return M_CAT
def XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma=VIEWING_CONDITIONS_RLAB['Average'], D=D_FACTOR_RLAB['Hard Copy Images']): """ Computes the *RLAB* model color appearance correlates. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of test sample / stimulus. XYZ_n : array_like *CIE XYZ* tristimulus values of reference white. Y_n : numeric or array_like Absolute adapting luminance in :math:`cd/m^2`. sigma : numeric or array_like, optional Relative luminance of the surround, see :attr:`colour.VIEWING_CONDITIONS_RLAB` for reference. D : numeric or array_like, optional *Discounting-the-Illuminant* factor normalised to domain [0, 1]. Returns ------- CAM_Specification_RLAB *RLAB* colour appearance model specification. Notes ----- +--------------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +==========================+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +--------------------------+-----------------------+---------------+ | ``XYZ_n`` | [0, 100] | [0, 1] | +--------------------------+-----------------------+---------------+ +------------------------------+-----------------------\ +---------------+ | **Range** | **Scale - Reference** \ | **Scale - 1** | +==============================+=======================\ +===============+ | ``CAM_Specification_RLAB.h`` | [0, 360] \ | [0, 1] | +------------------------------+-----------------------\ +---------------+ References ---------- :cite:`Fairchild1996a`, :cite:`Fairchild2013w` Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_n = np.array([109.85, 100, 35.58]) >>> Y_n = 31.83 >>> sigma = VIEWING_CONDITIONS_RLAB['Average'] >>> D = D_FACTOR_RLAB['Hard Copy Images'] >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D) # doctest: +ELLIPSIS CAM_Specification_RLAB(J=49.8347069..., C=54.8700585..., \ h=286.4860208..., s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...) """ XYZ = to_domain_100(XYZ) XYZ_n = to_domain_100(XYZ_n) Y_n = as_float_array(Y_n) D = as_float_array(D) sigma = as_float_array(sigma) # Converting to cone responses. LMS_n = XYZ_to_rgb(XYZ_n) # Computing the :math:`A` matrix. LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2]) LMS_p_L = ((1 + spow(Y_n[..., np.newaxis], 1 / 3) + LMS_l_E) / (1 + spow(Y_n[..., np.newaxis], 1 / 3) + (1 / LMS_l_E))) LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n aR = row_as_diagonal(LMS_a_L) M = matrix_dot(matrix_dot(MATRIX_R, aR), MATRIX_XYZ_TO_HPE) XYZ_ref = vector_dot(M, XYZ) X_ref, Y_ref, Z_ref = tsplit(XYZ_ref) # Computing the correlate of *Lightness* :math:`L^R`. LR = 100 * spow(Y_ref, sigma) # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`. aR = 430 * (spow(X_ref, sigma) - spow(Y_ref, sigma)) bR = 170 * (spow(Y_ref, sigma) - spow(Z_ref, sigma)) # Computing the *hue* angle :math:`h^R`. hR = np.degrees(np.arctan2(bR, aR)) % 360 # TODO: Implement hue composition computation. # Computing the correlate of *chroma* :math:`C^R`. CR = np.hypot(aR, bR) # Computing the correlate of *saturation* :math:`s^R`. sR = CR / LR return CAM_Specification_RLAB(LR, CR, from_range_degrees(hR), sR, None, aR, bR)
def XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma=RLAB_VIEWING_CONDITIONS.get('Average'), D=RLAB_D_FACTOR.get('Hard Copy Images')): """ Computes the RLAB model color appearance correlates. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of test sample / stimulus in domain [0, 100]. XYZ_n : array_like *CIE XYZ* tristimulus values of reference white in domain [0, 100]. Y_n : numeric or array_like Absolute adapting luminance in :math:`cd/m^2`. sigma : numeric or array_like, optional Relative luminance of the surround, see :attr:`RLAB_VIEWING_CONDITIONS` for reference. D : numeric or array_like, optional *Discounting-the-Illuminant* factor in domain [0, 1]. Returns ------- RLAB_Specification RLAB colour appearance model specification. Warning ------- The input domain of that definition is non standard! Notes ----- - Input *CIE XYZ* tristimulus values are in domain [0, 100]. - Input *CIE XYZ_n* tristimulus values are in domain [0, 100]. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_n = np.array([109.85, 100, 35.58]) >>> Y_n = 31.83 >>> sigma = RLAB_VIEWING_CONDITIONS['Average'] >>> D = RLAB_D_FACTOR['Hard Copy Images'] >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D) # doctest: +ELLIPSIS RLAB_Specification(J=49.8347069..., C=54.8700585..., h=286.4860208..., \ s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...) """ Y_n = np.asarray(Y_n) D = np.asarray(D) sigma = np.asarray(sigma) # Converting to cone responses. LMS_n = XYZ_to_rgb(XYZ_n) # Computing the :math:`A` matrix. LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2]) LMS_p_L = ((1 + (Y_n[..., np.newaxis] ** (1 / 3)) + LMS_l_E) / (1 + (Y_n[..., np.newaxis] ** (1 / 3)) + (1 / LMS_l_E))) LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n aR = row_as_diagonal(LMS_a_L) M = dot_matrix(dot_matrix(R_MATRIX, aR), XYZ_TO_HPE_MATRIX) XYZ_ref = dot_vector(M, XYZ) X_ref, Y_ref, Z_ref = tsplit(XYZ_ref) # ------------------------------------------------------------------------- # Computing the correlate of *Lightness* :math:`L^R`. # ------------------------------------------------------------------------- LR = 100 * (Y_ref ** sigma) # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`. aR = 430 * ((X_ref ** sigma) - (Y_ref ** sigma)) bR = 170 * ((Y_ref ** sigma) - (Z_ref ** sigma)) # ------------------------------------------------------------------------- # Computing the *hue* angle :math:`h^R`. # ------------------------------------------------------------------------- hR = np.degrees(np.arctan2(bR, aR)) % 360 # TODO: Implement hue composition computation. # ------------------------------------------------------------------------- # Computing the correlate of *chroma* :math:`C^R`. # ------------------------------------------------------------------------- CR = np.sqrt((aR ** 2) + (bR ** 2)) # ------------------------------------------------------------------------- # Computing the correlate of *saturation* :math:`s^R`. # ------------------------------------------------------------------------- sR = CR / LR return RLAB_Specification(LR, CR, hR, sR, None, aR, bR)
def XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma=RLAB_VIEWING_CONDITIONS['Average'], D=RLAB_D_FACTOR['Hard Copy Images']): """ Computes the *RLAB* model color appearance correlates. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of test sample / stimulus in domain [0, 100]. XYZ_n : array_like *CIE XYZ* tristimulus values of reference white in domain [0, 100]. Y_n : numeric or array_like Absolute adapting luminance in :math:`cd/m^2`. sigma : numeric or array_like, optional Relative luminance of the surround, see :attr:`colour.RLAB_VIEWING_CONDITIONS` for reference. D : numeric or array_like, optional *Discounting-the-Illuminant* factor in domain [0, 1]. Returns ------- RLAB_Specification *RLAB* colour appearance model specification. Warning ------- The input domain of that definition is non standard! Notes ----- - Input *CIE XYZ* tristimulus values are in domain [0, 100]. - Input *CIE XYZ_n* tristimulus values are in domain [0, 100]. References ---------- - :cite:`Fairchild1996a` - :cite:`Fairchild2013w` Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_n = np.array([109.85, 100, 35.58]) >>> Y_n = 31.83 >>> sigma = RLAB_VIEWING_CONDITIONS['Average'] >>> D = RLAB_D_FACTOR['Hard Copy Images'] >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D) # doctest: +ELLIPSIS RLAB_Specification(J=49.8347069..., C=54.8700585..., h=286.4860208..., \ s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...) """ Y_n = np.asarray(Y_n) D = np.asarray(D) sigma = np.asarray(sigma) # Converting to cone responses. LMS_n = XYZ_to_rgb(XYZ_n) # Computing the :math:`A` matrix. LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2]) LMS_p_L = ((1 + (Y_n[..., np.newaxis] ** (1 / 3)) + LMS_l_E) / (1 + (Y_n[..., np.newaxis] ** (1 / 3)) + (1 / LMS_l_E))) LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n aR = row_as_diagonal(LMS_a_L) M = dot_matrix(dot_matrix(R_MATRIX, aR), XYZ_TO_HPE_MATRIX) XYZ_ref = dot_vector(M, XYZ) X_ref, Y_ref, Z_ref = tsplit(XYZ_ref) # Computing the correlate of *Lightness* :math:`L^R`. LR = 100 * (Y_ref ** sigma) # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`. aR = 430 * ((X_ref ** sigma) - (Y_ref ** sigma)) bR = 170 * ((Y_ref ** sigma) - (Z_ref ** sigma)) # Computing the *hue* angle :math:`h^R`. hR = np.degrees(np.arctan2(bR, aR)) % 360 # TODO: Implement hue composition computation. # Computing the correlate of *chroma* :math:`C^R`. CR = np.hypot(aR, bR) # Computing the correlate of *saturation* :math:`s^R`. sR = CR / LR return RLAB_Specification(LR, CR, hR, sR, None, aR, bR)
def chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n, discount_illuminant=False): """ Adapts given stimulus *CIE XYZ_1* tristimulus values from test viewing conditions to reference viewing conditions using Fairchild (1990) chromatic adaptation model. Parameters ---------- XYZ_1 : array_like *CIE XYZ_1* tristimulus values of test sample / stimulus in domain [0, 100]. XYZ_n : array_like Test viewing condition *CIE XYZ_n* tristimulus values of whitepoint. XYZ_r : array_like Reference viewing condition *CIE XYZ_r* tristimulus values of whitepoint. Y_n : numeric or array_like Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Returns ------- ndarray Adapted *CIE XYZ_2* tristimulus values of stimulus. Warning ------- The input domain of that definition is non standard! Notes ----- - Input *CIE XYZ_1*, *CIE XYZ_n* and *CIE XYZ_r* tristimulus values are in domain [0, 100]. - Output *CIE XYZ_2* tristimulus values are in domain [0, 100]. Examples -------- >>> XYZ_1 = np.array([19.53, 23.07, 24.97]) >>> XYZ_n = np.array([111.15, 100.00, 35.20]) >>> XYZ_r = np.array([94.81, 100.00, 107.30]) >>> Y_n = 200 >>> chromatic_adaptation_Fairchild1990( # doctest: +ELLIPSIS ... XYZ_1, XYZ_n, XYZ_r, Y_n) array([ 23.3252634..., 23.3245581..., 76.1159375...]) """ XYZ_1 = np.asarray(XYZ_1) XYZ_n = np.asarray(XYZ_n) XYZ_r = np.asarray(XYZ_r) Y_n = np.asarray(Y_n) LMS_1 = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_1) LMS_n = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_n) LMS_r = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_r) p_LMS = degrees_of_adaptation(LMS_1, Y_n, discount_illuminant=discount_illuminant) a_LMS_1 = p_LMS / LMS_n a_LMS_2 = p_LMS / LMS_r A_1 = row_as_diagonal(a_LMS_1) A_2 = row_as_diagonal(a_LMS_2) LMSp_1 = dot_vector(A_1, LMS_1) c = 0.219 - 0.0784 * np.log10(Y_n) C = row_as_diagonal(tstack((c, c, c))) LMS_a = dot_vector(C, LMSp_1) LMSp_2 = dot_vector(np.linalg.inv(C), LMS_a) LMS_c = dot_vector(np.linalg.inv(A_2), LMSp_2) XYZ_c = dot_vector(FAIRCHILD1990_RGB_TO_XYZ_MATRIX, LMS_c) return XYZ_c
def chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n, discount_illuminant=False): """ Adapts given stimulus *CIE XYZ_1* tristimulus values from test viewing conditions to reference viewing conditions using *Fairchild (1990)* chromatic adaptation model. Parameters ---------- XYZ_1 : array_like *CIE XYZ_1* tristimulus values of test sample / stimulus. XYZ_n : array_like Test viewing condition *CIE XYZ_n* tristimulus values of whitepoint. XYZ_r : array_like Reference viewing condition *CIE XYZ_r* tristimulus values of whitepoint. Y_n : numeric or array_like Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Returns ------- ndarray Adapted *CIE XYZ_2* tristimulus values of stimulus. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_1`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_n`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_r`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_2`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Fairchild1991a`, :cite:`Fairchild2013s` Examples -------- >>> XYZ_1 = np.array([19.53, 23.07, 24.97]) >>> XYZ_n = np.array([111.15, 100.00, 35.20]) >>> XYZ_r = np.array([94.81, 100.00, 107.30]) >>> Y_n = 200 >>> chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n) ... # doctest: +ELLIPSIS array([ 23.3252634..., 23.3245581..., 76.1159375...]) """ XYZ_1 = to_domain_100(XYZ_1) XYZ_n = to_domain_100(XYZ_n) XYZ_r = to_domain_100(XYZ_r) Y_n = as_float_array(Y_n) LMS_1 = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_1) LMS_n = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_n) LMS_r = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_r) p_LMS = degrees_of_adaptation( LMS_1, Y_n, discount_illuminant=discount_illuminant) a_LMS_1 = p_LMS / LMS_n a_LMS_2 = p_LMS / LMS_r A_1 = row_as_diagonal(a_LMS_1) A_2 = row_as_diagonal(a_LMS_2) LMSp_1 = dot_vector(A_1, LMS_1) c = 0.219 - 0.0784 * np.log10(Y_n) C = row_as_diagonal(tstack([c, c, c])) LMS_a = dot_vector(C, LMSp_1) LMSp_2 = dot_vector(np.linalg.inv(C), LMS_a) LMS_c = dot_vector(np.linalg.inv(A_2), LMSp_2) XYZ_c = dot_vector(FAIRCHILD1990_RGB_TO_XYZ_MATRIX, LMS_c) return from_range_100(XYZ_c)
def chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform='CAT02'): """ Computes the *chromatic adaptation* matrix from test viewing conditions to reference viewing conditions. Parameters ---------- XYZ_w : array_like Test viewing condition *CIE XYZ* tristimulus values of whitepoint. XYZ_wr : array_like Reference viewing condition *CIE XYZ* tristimulus values of whitepoint. transform : unicode, optional **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco', 'Bianco PC'}**, Chromatic adaptation transform. Returns ------- ndarray Chromatic adaptation matrix. Raises ------ KeyError If chromatic adaptation method is not defined. Examples -------- >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280]) >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037]) >>> chromatic_adaptation_matrix_VonKries( # doctest: +ELLIPSIS ... XYZ_w, XYZ_wr) array([[ 0.8687653..., -0.1416539..., 0.3871961...], [-0.1030072..., 1.0584014..., 0.1538646...], [ 0.0078167..., 0.0267875..., 2.9608177...]]) Using Bradford method: >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280]) >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037]) >>> method = 'Bradford' >>> chromatic_adaptation_matrix_VonKries( # doctest: +ELLIPSIS ... XYZ_w, XYZ_wr, method) array([[ 0.8446794..., -0.1179355..., 0.3948940...], [-0.1366408..., 1.1041236..., 0.1291981...], [ 0.0798671..., -0.1349315..., 3.1928829...]]) """ M = CHROMATIC_ADAPTATION_TRANSFORMS.get(transform) if M is None: raise KeyError( '"{0}" chromatic adaptation transform is not defined! Supported ' 'methods: "{1}".'.format(transform, CHROMATIC_ADAPTATION_TRANSFORMS.keys())) rgb_w = np.einsum('...i,...ij->...j', XYZ_w, np.transpose(M)) rgb_wr = np.einsum('...i,...ij->...j', XYZ_wr, np.transpose(M)) D = rgb_wr / rgb_w D = row_as_diagonal(D) cat = dot_matrix(np.linalg.inv(M), D) cat = dot_matrix(cat, M) return cat
def chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n, discount_illuminant=False): """ Adapts given stimulus *CIE XYZ_1* tristimulus values from test viewing conditions to reference viewing conditions using *Fairchild (1990)* chromatic adaptation model. Parameters ---------- XYZ_1 : array_like *CIE XYZ_1* tristimulus values of test sample / stimulus in domain [0, 100]. XYZ_n : array_like Test viewing condition *CIE XYZ_n* tristimulus values of whitepoint. XYZ_r : array_like Reference viewing condition *CIE XYZ_r* tristimulus values of whitepoint. Y_n : numeric or array_like Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Returns ------- ndarray Adapted *CIE XYZ_2* tristimulus values of stimulus. Warning ------- The input domain and output range of that definition are non standard! Notes ----- - Input *CIE XYZ_1*, *CIE XYZ_n* and *CIE XYZ_r* tristimulus values are in domain [0, 100]. - Output *CIE XYZ_2* tristimulus values are in range [0, 100]. References ---------- - :cite:`Fairchild1991a` - :cite:`Fairchild2013s` Examples -------- >>> XYZ_1 = np.array([19.53, 23.07, 24.97]) >>> XYZ_n = np.array([111.15, 100.00, 35.20]) >>> XYZ_r = np.array([94.81, 100.00, 107.30]) >>> Y_n = 200 >>> chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n) ... # doctest: +ELLIPSIS array([ 23.3252634..., 23.3245581..., 76.1159375...]) """ XYZ_1 = np.asarray(XYZ_1) XYZ_n = np.asarray(XYZ_n) XYZ_r = np.asarray(XYZ_r) Y_n = np.asarray(Y_n) LMS_1 = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_1) LMS_n = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_n) LMS_r = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_r) p_LMS = degrees_of_adaptation( LMS_1, Y_n, discount_illuminant=discount_illuminant) a_LMS_1 = p_LMS / LMS_n a_LMS_2 = p_LMS / LMS_r A_1 = row_as_diagonal(a_LMS_1) A_2 = row_as_diagonal(a_LMS_2) LMSp_1 = dot_vector(A_1, LMS_1) c = 0.219 - 0.0784 * np.log10(Y_n) C = row_as_diagonal(tstack((c, c, c))) LMS_a = dot_vector(C, LMSp_1) LMSp_2 = dot_vector(np.linalg.inv(C), LMS_a) LMS_c = dot_vector(np.linalg.inv(A_2), LMSp_2) XYZ_c = dot_vector(FAIRCHILD1990_RGB_TO_XYZ_MATRIX, LMS_c) return XYZ_c
def XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma=RLAB_VIEWING_CONDITIONS['Average'], D=RLAB_D_FACTOR['Hard Copy Images']): """ Computes the *RLAB* model color appearance correlates. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of test sample / stimulus. XYZ_n : array_like *CIE XYZ* tristimulus values of reference white. Y_n : numeric or array_like Absolute adapting luminance in :math:`cd/m^2`. sigma : numeric or array_like, optional Relative luminance of the surround, see :attr:`colour.RLAB_VIEWING_CONDITIONS` for reference. D : numeric or array_like, optional *Discounting-the-Illuminant* factor normalised to domain [0, 1]. Returns ------- RLAB_Specification *RLAB* colour appearance model specification. Notes ----- +--------------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +==========================+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +--------------------------+-----------------------+---------------+ | ``XYZ_n`` | [0, 100] | [0, 1] | +--------------------------+-----------------------+---------------+ +--------------------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +==========================+=======================+===============+ | ``RLAB_Specification.h`` | [0, 360] | [0, 1] | +--------------------------+-----------------------+---------------+ References ---------- :cite:`Fairchild1996a`, :cite:`Fairchild2013w` Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_n = np.array([109.85, 100, 35.58]) >>> Y_n = 31.83 >>> sigma = RLAB_VIEWING_CONDITIONS['Average'] >>> D = RLAB_D_FACTOR['Hard Copy Images'] >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D) # doctest: +ELLIPSIS RLAB_Specification(J=49.8347069..., C=54.8700585..., h=286.4860208..., \ s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...) """ XYZ = to_domain_100(XYZ) XYZ_n = to_domain_100(XYZ_n) Y_n = as_float_array(Y_n) D = as_float_array(D) sigma = as_float_array(sigma) # Converting to cone responses. LMS_n = XYZ_to_rgb(XYZ_n) # Computing the :math:`A` matrix. LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2]) LMS_p_L = ((1 + spow(Y_n[..., np.newaxis], 1 / 3) + LMS_l_E) / (1 + spow(Y_n[..., np.newaxis], 1 / 3) + (1 / LMS_l_E))) LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n aR = row_as_diagonal(LMS_a_L) M = dot_matrix(dot_matrix(R_MATRIX, aR), XYZ_TO_HPE_MATRIX) XYZ_ref = dot_vector(M, XYZ) X_ref, Y_ref, Z_ref = tsplit(XYZ_ref) # Computing the correlate of *Lightness* :math:`L^R`. LR = 100 * spow(Y_ref, sigma) # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`. aR = 430 * (spow(X_ref, sigma) - spow(Y_ref, sigma)) bR = 170 * (spow(Y_ref, sigma) - spow(Z_ref, sigma)) # Computing the *hue* angle :math:`h^R`. hR = np.degrees(np.arctan2(bR, aR)) % 360 # TODO: Implement hue composition computation. # Computing the correlate of *chroma* :math:`C^R`. CR = np.hypot(aR, bR) # Computing the correlate of *saturation* :math:`s^R`. sR = CR / LR return RLAB_Specification(LR, CR, from_range_degrees(hR), sR, None, aR, bR)
def matrix_chromatic_adaptation_VonKries( XYZ_w: ArrayLike, XYZ_wr: ArrayLike, transform: Union[Literal["Bianco 2010", "Bianco PC 2010", "Bradford", "CAT02 Brill 2008", "CAT02", "CAT16", "CMCCAT2000", "CMCCAT97", "Fairchild", "Sharp", "Von Kries", "XYZ Scaling", ], str, ] = "CAT02", ) -> NDArray: """ Compute the *chromatic adaptation* matrix from test viewing conditions to reference viewing conditions. Parameters ---------- XYZ_w Test viewing conditions *CIE XYZ* tristimulus values of whitepoint. XYZ_wr Reference viewing conditions *CIE XYZ* tristimulus values of whitepoint. transform Chromatic adaptation transform. Returns ------- :class:`numpy.ndarray` Chromatic adaptation matrix :math:`M_{cat}`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_w`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wr`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Fairchild2013t` Examples -------- >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr) ... # doctest: +ELLIPSIS array([[ 1.0425738..., 0.0308910..., -0.0528125...], [ 0.0221934..., 1.0018566..., -0.0210737...], [-0.0011648..., -0.0034205..., 0.7617890...]]) Using Bradford method: >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> method = 'Bradford' >>> matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr, method) ... # doctest: +ELLIPSIS array([[ 1.0479297..., 0.0229468..., -0.0501922...], [ 0.0296278..., 0.9904344..., -0.0170738...], [-0.0092430..., 0.0150551..., 0.7518742...]]) """ XYZ_w = to_domain_1(XYZ_w) XYZ_wr = to_domain_1(XYZ_wr) transform = validate_method( transform, CHROMATIC_ADAPTATION_TRANSFORMS, '"{0}" chromatic adaptation transform is invalid, ' "it must be one of {1}!", ) M = CHROMATIC_ADAPTATION_TRANSFORMS[transform] RGB_w = np.einsum("...i,...ij->...j", XYZ_w, np.transpose(M)) RGB_wr = np.einsum("...i,...ij->...j", XYZ_wr, np.transpose(M)) D = RGB_wr / RGB_w D = row_as_diagonal(D) M_CAT = matrix_dot(np.linalg.inv(M), D) M_CAT = matrix_dot(M_CAT, M) return M_CAT