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 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, (3, n) *CIE XYZ* colourspace matrix of test sample / stimulus in domain [0, 100]. XYZ_n : array_like, (3,) *CIE XYZ* colourspace matrix of reference white in domain [0, 100]. Y_n : numeric Absolute adapting luminance in :math:`cd/m^2`. sigma : numeric, optional Relative luminance of the surround, see :attr:`RLAB_VIEWING_CONDITIONS` for reference. D : numeric, 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* colourspace matrix is in domain [0, 100]. - Input *CIE XYZ_n* colourspace matrix is in domain [0, 100]. Examples -------- >>> XYZ = np.array([19.01, 20, 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...) """ X, Y, Z = np.ravel(XYZ) # Converting to cone responses. LMS = XYZ_to_rgb(XYZ) 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**(1 / 3)) + LMS_l_E) / (1 + (Y_n**(1 / 3)) + (1 / LMS_l_E))) LMS_a_L = (LMS_p_L + D * (1 - LMS_p_L)) / LMS_n # Special handling here to allow *array_like* variable input. if len(np.shape(X)) == 0: # *numeric* case. # Implementation as per reference. aR = np.diag(LMS_a_L) XYZ_ref = R_MATRIX.dot(aR).dot(XYZ_TO_HPE_MATRIX).dot(XYZ) else: # *array_like* case. # Constructing huge multidimensional arrays might not be the best idea, # we handle each input dimension separately. # First figure out how many values we have to deal with. dimension = len(X) # Then create the output array that will be filled layer by layer. XYZ_ref = np.zeros((3, dimension)) for layer in range(dimension): aR = np.diag(LMS_a_L[..., layer]) XYZ_ref[..., layer] = (R_MATRIX.dot(aR).dot(XYZ_TO_HPE_MATRIX).dot( XYZ[..., layer])) X_ref, Y_ref, Z_ref = 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 = math.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)