def XYZ_to_LMS_ATD95(XYZ): """ Converts from *CIE XYZ* tristimulus values to *LMS* cone responses. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray *LMS* cone responses. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_to_LMS_ATD95(XYZ) # doctest: +ELLIPSIS array([ 6.2283272..., 7.4780666..., 3.8859772...]) """ X, Y, Z = tsplit(XYZ) L = ((0.66 * (0.2435 * X + 0.8524 * Y - 0.0516 * Z))**0.7) + 0.024 M = ((-0.3954 * X + 1.1642 * Y + 0.0837 * Z)**0.7) + 0.036 S = ((0.43 * (0.04 * Y + 0.6225 * Z))**0.7) + 0.31 LMS = tstack((L, M, S)) return LMS
def XYZ_to_LMS_ATD95(XYZ): """ Converts from *CIE XYZ* tristimulus values to *LMS* cone responses. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray *LMS* cone responses. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_to_LMS_ATD95(XYZ) # doctest: +ELLIPSIS array([ 6.2283272..., 7.4780666..., 3.8859772...]) """ X, Y, Z = tsplit(XYZ) L = ((0.66 * (0.2435 * X + 0.8524 * Y - 0.0516 * Z)) ** 0.7) + 0.024 M = ((-0.3954 * X + 1.1642 * Y + 0.0837 * Z) ** 0.7) + 0.036 S = ((0.43 * (0.04 * Y + 0.6225 * Z)) ** 0.7) + 0.31 LMS = tstack((L, M, S)) return LMS
def opponent_colour_dimensions(LMS_g): """ Returns opponent colour dimensions from given post adaptation cone signals. Parameters ---------- LMS_g : array_like Post adaptation cone signals. Returns ------- ndarray Opponent colour dimensions. Examples -------- >>> LMS_g = np.array([6.95457922, 7.08945043, 6.44069316]) >>> opponent_colour_dimensions(LMS_g) # doctest: +ELLIPSIS array([ 0.1787931..., 0.0286942..., 0.0107584..., 0.0192182..., 0.0205377..., 0.0107584...]) """ L_g, M_g, S_g = tsplit(LMS_g) A_1i = 3.57 * L_g + 2.64 * M_g T_1i = 7.18 * L_g - 6.21 * M_g D_1i = -0.7 * L_g + 0.085 * M_g + S_g A_2i = 0.09 * A_1i T_2i = 0.43 * T_1i + 0.76 * D_1i D_2i = D_1i A_1 = final_response(A_1i) T_1 = final_response(T_1i) D_1 = final_response(D_1i) A_2 = final_response(A_2i) T_2 = final_response(T_2i) D_2 = final_response(D_2i) return tstack((A_1, T_1, D_1, A_2, T_2, D_2))
def opponent_colour_dimensions(LMS_g): """ Returns opponent colour dimensions from given post adaptation cone signals. Parameters ---------- LMS_g : array_like Post adaptation cone signals. Returns ------- ndarray Opponent colour dimensions. Examples -------- >>> LMS_g = np.array([6.95457922, 7.08945043, 6.44069316]) >>> opponent_colour_dimensions(LMS_g) # doctest: +ELLIPSIS array([ 0.1787931..., 0.0286942..., 0.0107584..., 0.0192182..., ...]) """ L_g, M_g, S_g = tsplit(LMS_g) A_1i = 3.57 * L_g + 2.64 * M_g T_1i = 7.18 * L_g - 6.21 * M_g D_1i = -0.7 * L_g + 0.085 * M_g + S_g A_2i = 0.09 * A_1i T_2i = 0.43 * T_1i + 0.76 * D_1i D_2i = D_1i A_1 = final_response(A_1i) T_1 = final_response(T_1i) D_1 = final_response(D_1i) A_2 = final_response(A_2i) T_2 = final_response(T_2i) D_2 = final_response(D_2i) return tstack((A_1, T_1, D_1, A_2, T_2, D_2))
def XYZ_to_ATD95(XYZ, XYZ_0, Y_0, k_1, k_2, sigma=300): """ Computes the ATD (1995) colour vision model correlates. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of test sample / stimulus in domain [0, 100]. XYZ_0 : array_like *CIE XYZ* tristimulus values of reference white in domain [0, 100]. Y_0 : numeric or array_like Absolute adapting field luminance in :math:`cd/m^2`. k_1 : numeric or array_like Application specific weight :math:`k_1`. k_2 : numeric or array_like Application specific weight :math:`k_2`. sigma : numeric or array_like, optional Constant :math:`\sigma` varied to predict different types of data. Returns ------- ATD95_Specification ATD (1995) colour vision 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_0* tristimulus values are in domain [0, 100]. - For unrelated colors, there is only self-adaptation, and :math:`k_1` is set to 1.0 while :math:`k_2` is set to 0.0. For related colors such as typical colorimetric applications, :math:`k_1` is set to 0.0 and :math:`k_2` is set to a value between 15 and 50 *(Guth, 1995)*. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_0 = np.array([95.05, 100.00, 108.88]) >>> Y_0 = 318.31 >>> k_1 = 0.0 >>> k_2 = 50.0 >>> XYZ_to_ATD95(XYZ, XYZ_0, Y_0, k_1, k_2) # doctest: +ELLIPSIS ATD95_Specification(h=1.9089869..., C=1.2064060..., Q=0.1814003..., \ A_1=0.1787931... T_1=0.0286942..., D_1=0.0107584..., A_2=0.0192182..., \ T_2=0.0205377..., D_2=0.0107584...) """ Y_0 = np.asarray(Y_0) k_1 = np.asarray(k_1) k_2 = np.asarray(k_2) sigma = np.asarray(sigma) XYZ = luminance_to_retinal_illuminance(XYZ, Y_0) XYZ_0 = luminance_to_retinal_illuminance(XYZ_0, Y_0) # Computing adaptation model. LMS = XYZ_to_LMS_ATD95(XYZ) XYZ_a = k_1[..., np.newaxis] * XYZ + k_2[..., np.newaxis] * XYZ_0 LMS_a = XYZ_to_LMS_ATD95(XYZ_a) LMS_g = LMS * (sigma[..., np.newaxis] / (sigma[..., np.newaxis] + LMS_a)) # Computing opponent colour dimensions. A_1, T_1, D_1, A_2, T_2, D_2 = tsplit(opponent_colour_dimensions(LMS_g)) # ------------------------------------------------------------------------- # Computing the correlate of *brightness* :math:`Br`. # ------------------------------------------------------------------------- Br = (A_1**2 + T_1**2 + D_1**2)**0.5 # ------------------------------------------------------------------------- # Computing the correlate of *saturation* :math:`C`. # ------------------------------------------------------------------------- C = (T_2**2 + D_2**2)**0.5 / A_2 # ------------------------------------------------------------------------- # Computing the *hue* :math:`H`. # ------------------------------------------------------------------------- H = T_2 / D_2 return ATD95_Specification(H, C, Br, A_1, T_1, D_1, A_2, T_2, D_2)
def XYZ_to_ATD95(XYZ, XYZ_0, Y_0, k_1, k_2, sigma=300): """ Computes the ATD (1995) colour vision model correlates. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of test sample / stimulus in domain [0, 100]. XYZ_0 : array_like *CIE XYZ* tristimulus values of reference white in domain [0, 100]. Y_0 : numeric or array_like Absolute adapting field luminance in :math:`cd/m^2`. k_1 : numeric or array_like Application specific weight :math:`k_1`. k_2 : numeric or array_like Application specific weight :math:`k_2`. sigma : numeric or array_like, optional Constant :math:`\sigma` varied to predict different types of data. Returns ------- ATD95_Specification ATD (1995) colour vision 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_0* tristimulus values are in domain [0, 100]. - For unrelated colors, there is only self-adaptation, and :math:`k_1` is set to 1.0 while :math:`k_2` is set to 0.0. For related colors such as typical colorimetric applications, :math:`k_1` is set to 0.0 and :math:`k_2` is set to a value between 15 and 50 *(Guth, 1995)*. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_0 = np.array([95.05, 100.00, 108.88]) >>> Y_0 = 318.31 >>> k_1 = 0.0 >>> k_2 = 50.0 >>> XYZ_to_ATD95(XYZ, XYZ_0, Y_0, k_1, k_2) # doctest: +ELLIPSIS ATD95_Specification(h=1.9089869..., C=1.2064060..., Q=0.1814003..., \ A_1=0.1787931... T_1=0.0286942..., D_1=0.0107584..., A_2=0.0192182..., \ T_2=0.0205377..., D_2=0.0107584...) """ Y_0 = np.asarray(Y_0) k_1 = np.asarray(k_1) k_2 = np.asarray(k_2) sigma = np.asarray(sigma) XYZ = luminance_to_retinal_illuminance(XYZ, Y_0) XYZ_0 = luminance_to_retinal_illuminance(XYZ_0, Y_0) # Computing adaptation model. LMS = XYZ_to_LMS_ATD95(XYZ) XYZ_a = k_1[..., np.newaxis] * XYZ + k_2[..., np.newaxis] * XYZ_0 LMS_a = XYZ_to_LMS_ATD95(XYZ_a) LMS_g = LMS * (sigma[..., np.newaxis] / (sigma[..., np.newaxis] + LMS_a)) # Computing opponent colour dimensions. A_1, T_1, D_1, A_2, T_2, D_2 = tsplit( opponent_colour_dimensions(LMS_g)) # ------------------------------------------------------------------------- # Computing the correlate of *brightness* :math:`Br`. # ------------------------------------------------------------------------- Br = (A_1 ** 2 + T_1 ** 2 + D_1 ** 2) ** 0.5 # ------------------------------------------------------------------------- # Computing the correlate of *saturation* :math:`C`. # ------------------------------------------------------------------------- C = (T_2 ** 2 + D_2 ** 2) ** 0.5 / A_2 # ------------------------------------------------------------------------- # Computing the *hue* :math:`H`. # ------------------------------------------------------------------------- H = T_2 / D_2 return ATD95_Specification(H, C, Br, A_1, T_1, D_1, A_2, T_2, D_2)