def f(x: FloatingOrArrayLike, F_S: FloatingOrArrayLike) -> FloatingOrNDArray: """ Define the nonlinear response function of the *:math:`LLAB(l:c)`* colour appearance model used to model the nonlinear behaviour of various visual responses. Parameters ---------- x Visual response variable :math:`x`. F_S Surround induction factor :math:`F_S`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Modeled visual response variable :math:`x`. Examples -------- >>> x = np.array([0.23350512, 0.23351103, 0.23355179]) >>> f(0.200009186234000, 3) # doctest: +ELLIPSIS 0.5848125... """ x = as_float_array(x) F_S = as_float_array(F_S) x_m = np.where( x > 0.008856, spow(x, 1 / F_S), ((spow(0.008856, 1 / F_S) - (16 / 116)) / 0.008856) * x + (16 / 116), ) return as_float(x_m)
def f(x, F_S): """ Defines the nonlinear response function of the *:math:`LLAB(l:c)`* colour appearance model used to model the nonlinear behaviour of various visual responses. Parameters ---------- x : numeric or array_like or array_like Visual response variable :math:`x`. F_S : numeric or array_like Surround induction factor :math:`F_S`. Returns ------- numeric or array_like Modeled visual response variable :math:`x`. Examples -------- >>> x = np.array([0.23350512, 0.23351103, 0.23355179]) >>> f(0.200009186234000, 3) # doctest: +ELLIPSIS array(0.5848125...) """ x = as_float_array(x) F_S = as_float_array(F_S) x_m = np.where( x > 0.008856, spow(x, 1 / F_S), ((spow(0.008856, 1 / F_S) - (16 / 116)) / 0.008856) * x + (16 / 116), ) return x_m
def chroma_components(L_star_P, S_RG, S_YB): """ Returns the *chroma* components :math:`C_{RG}` and :math:`C_{YB}`. Parameters ---------- L_star_P : numeric or array_like *Achromatic Lightness* correlate :math:`L_p^\\star`. S_RG : numeric or array_like *Saturation* component :math:`S_{RG}`. S_YB : numeric or array_like *Saturation* component :math:`S_{YB}`. Returns ------- ndarray *Chroma* components :math:`C_{RG}` and :math:`C_{YB}`. Examples -------- >>> L_star_P = 49.99988297570504 >>> S_RG = -0.002885271638197 >>> S_YB = -0.013039632941332 >>> chroma_components(L_star_P, S_RG, S_YB) # doctest: +ELLIPSIS array([-0.00288527, -0.01303961]) """ L_star_P = as_float_array(L_star_P) S_RG = as_float_array(S_RG) S_YB = as_float_array(S_YB) C_RG = spow(L_star_P / 50, 0.7) * S_RG C_YB = spow(L_star_P / 50, 0.7) * S_YB return tstack([C_RG, C_YB])
def RGB_c(x_1, x_2, y_1, y_2, z): """ Computes the corresponding colour cone responses component. """ return ((Y_o * x_2 + n) * spow(K, 1 / y_2) * spow( (z + n) / (Y_o * x_1 + n), y_1 / y_2) - n)
def chroma_correlate( J: FloatingOrArrayLike, n: FloatingOrArrayLike, N_c: FloatingOrArrayLike, N_cb: FloatingOrArrayLike, e_t: FloatingOrArrayLike, a: FloatingOrArrayLike, b: FloatingOrArrayLike, RGB_a: ArrayLike, ) -> FloatingOrNDArray: """ Return the *chroma* correlate :math:`C`. Parameters ---------- J *Lightness* correlate :math:`J`. n Function of the luminance factor of the background :math:`n`. N_c Surround chromatic induction factor :math:`N_{c}`. N_cb Chromatic induction factor :math:`N_{cb}`. e_t Eccentricity factor :math:`e_t`. a Opponent colour dimension :math:`a`. b Opponent colour dimension :math:`b`. RGB_a Compressed stimulus *CMCCAT2000* transform sharpened *RGB* array. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Chroma* correlate :math:`C`. Examples -------- >>> J = 41.7310911325 >>> n = 0.2 >>> N_c = 1.0 >>> N_cb = 1.00030400456 >>> e_t = 1.17400547285 >>> a = -0.000624112068243 >>> b = -0.000506270106773 >>> RGB_a = np.array([7.94632020, 7.94711528, 7.94899595]) >>> chroma_correlate(J, n, N_c, N_cb, e_t, a, b, RGB_a) ... # doctest: +ELLIPSIS 0.1047077... """ J = as_float_array(J) n = as_float_array(n) t = temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a) C = spow(t, 0.9) * spow(J / 100, 0.5) * spow(1.64 - 0.29**n, 0.73) return C
def chromatic_adaptation( RGB: ArrayLike, RGB_0: ArrayLike, RGB_0r: ArrayLike, Y: FloatingOrArrayLike, D: FloatingOrArrayLike = 1, ) -> NDArray: """ Apply chromatic adaptation to given *RGB* normalised cone responses array. Parameters ---------- RGB *RGB* normalised cone responses array of test sample / stimulus. RGB_0 *RGB* normalised cone responses array of reference white. RGB_0r *RGB* normalised cone responses array of reference illuminant *CIE Standard Illuminant D Series* *D65*. Y Tristimulus values :math:`Y` of the stimulus. D *Discounting-the-Illuminant* factor normalised to domain [0, 1]. Returns ------- :class:`numpy.ndarray` Adapted *CIE XYZ* tristimulus values. Examples -------- >>> RGB = np.array([0.94142795, 1.04040120, 1.08970885]) >>> RGB_0 = np.array([0.94146023, 1.04039386, 1.08950293]) >>> RGB_0r = np.array([0.94146023, 1.04039386, 1.08950293]) >>> Y = 20.0 >>> chromatic_adaptation(RGB, RGB_0, RGB_0r, Y) # doctest: +ELLIPSIS array([ 19.01, 20. , 21.78]) """ R, G, B = tsplit(RGB) R_0, G_0, B_0 = tsplit(RGB_0) R_0r, G_0r, B_0r = tsplit(RGB_0r) Y = as_float_array(Y) D = as_float_array(D) beta = spow(B_0 / B_0r, 0.0834) R_r = (D * (R_0r / R_0) + 1 - D) * R G_r = (D * (G_0r / G_0) + 1 - D) * G B_r = (D * (B_0r / spow(B_0, beta)) + 1 - D) * spow(B, beta) RGB_r = tstack([R_r, G_r, B_r]) Y = tstack([Y, Y, Y]) XYZ_r = vector_dot(MATRIX_RGB_TO_XYZ_LLAB, RGB_r * Y) return XYZ_r
def achromatic_signal(L_AS, S, S_w, N_bb, A_a): """ Returns the achromatic signal :math:`A`. Parameters ---------- L_AS : numeric or array_like Scotopic luminance :math:`L_{AS}` of the illuminant. S : numeric or array_like Scotopic response :math:`S` to the stimulus. S_w : numeric or array_like Scotopic response :math:`S_w` for the reference white. N_bb : numeric or array_like Brightness background induction factor :math:`N_{bb}`. A_a: numeric or array_like Achromatic post adaptation signal of the stimulus :math:`A_a`. Returns ------- numeric or ndarray Achromatic signal :math:`A`. Examples -------- >>> L_AS = 769.9376286541402 >>> S = 20.0 >>> S_w = 100.0 >>> N_bb = 0.725000000000000 >>> A_a = 18.982718664838487 >>> achromatic_signal(L_AS, S, S_w, N_bb, A_a) # doctest: +ELLIPSIS 15.5068546... """ L_AS = as_float_array(L_AS) S = as_float_array(S) S_w = as_float_array(S_w) N_bb = as_float_array(N_bb) A_a = as_float_array(A_a) j = 0.00001 / ((5 * L_AS / 2.26) + 0.00001) # Computing scotopic luminance level adaptation factor :math:`F_{LS}`. F_LS = 3800 * (j**2) * (5 * L_AS / 2.26) F_LS += 0.2 * (spow(1 - (j**2), 0.4)) * (spow(5 * L_AS / 2.26, 1 / 6)) # Computing cone bleach factors :math:`B_S`. B_S = 0.5 / (1 + 0.3 * spow((5 * L_AS / 2.26) * (S / S_w), 0.3)) B_S += 0.5 / (1 + 5 * (5 * L_AS / 2.26)) # Computing adapted scotopic signal :math:`A_S`. A_S = (f_n(F_LS * S / S_w) * 3.05 * B_S) + 0.3 # Computing achromatic signal :math:`A`. A = N_bb * (A_a - 1 + A_S - 0.3 + np.sqrt((1 + (0.3**2)))) return A
def lightness_Fairchild2010( Y: FloatingOrArrayLike, epsilon: FloatingOrArrayLike = 1.836 ) -> FloatingOrNDArray: """ Compute *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using *Fairchild and Wyble (2010)* method according to *Michaelis-Menten* kinetics. Parameters ---------- Y *Luminance* :math:`Y`. epsilon :math:`\\epsilon` exponent. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* :math:`L_{hdr}`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_hdr`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Fairchild2010` Examples -------- >>> lightness_Fairchild2010(12.19722535 / 100) # doctest: +ELLIPSIS 31.9963902... """ Y = to_domain_1(Y) maximum_perception = 100 L_hdr = ( reaction_rate_MichaelisMenten_Michaelis1913( spow(Y, epsilon), maximum_perception, spow(0.184, epsilon) ) + 0.02 ) return as_float(from_range_100(L_hdr))
def K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, n=1): """ Computes the coefficient :math:`K` for correcting the difference between the test and references illuminances. Parameters ---------- xez_1: array_like Intermediate values :math:`\\xi_1`, :math:`\\eta_1`, :math:`\\zeta_1` for the test illuminant and background. xez_2: array_like Intermediate values :math:`\\xi_2`, :math:`\\eta_2`, :math:`\\zeta_2` for the reference illuminant and background. bRGB_o1: array_like Chromatic adaptation exponential factors :math:`\\beta_1(R_{o1})`, :math:`\\beta_1(G_{o1})` and :math:`\\beta_2(B_{o1})` of test sample. bRGB_o2: array_like Chromatic adaptation exponential factors :math:`\\beta_1(R_{o2})`, :math:`\\beta_1(G_{o2})` and :math:`\\beta_2(B_{o2})` of reference sample. Y_o : numeric or array_like Luminance factor :math:`Y_o` of achromatic background as percentage normalised to domain [18, 100] in **'Reference'** domain-range scale. n : numeric or array_like, optional Noise component in fundamental primary system. Returns ------- numeric or array_like Coefficient :math:`K`. Examples -------- >>> xez_1 = np.array([1.11857195, 0.93295530, 0.32680879]) >>> xez_2 = np.array([1.00000372, 1.00000176, 0.99999461]) >>> bRGB_o1 = np.array([3.74852518, 3.63920879, 2.78924811]) >>> bRGB_o2 = np.array([3.68102374, 3.68102256, 3.56557351]) >>> Y_o = 20 >>> K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o) 1.0 """ xi_1, eta_1, _zeta_1 = tsplit(xez_1) xi_2, eta_2, _zeta_2 = tsplit(xez_2) bR_o1, bG_o1, _bB_o1 = tsplit(bRGB_o1) bR_o2, bG_o2, _bB_o2 = tsplit(bRGB_o2) Y_o = as_float_array(Y_o) K = (spow((Y_o * xi_1 + n) / (20 * xi_1 + n), (2 / 3) * bR_o1) / spow( (Y_o * xi_2 + n) / (20 * xi_2 + n), (2 / 3) * bR_o2)) K *= (spow((Y_o * eta_1 + n) / (20 * eta_1 + n), (1 / 3) * bG_o1) / spow( (Y_o * eta_2 + n) / (20 * eta_2 + n), (1 / 3) * bG_o2)) return K
def opponent_colour_dimensions( XYZ: ArrayLike, Y_b: FloatingOrArrayLike, F_S: FloatingOrArrayLike, F_L: FloatingOrArrayLike, ) -> NDArray: """ Return opponent colour dimensions from given adapted *CIE XYZ* tristimulus values. The opponent colour dimensions are based on a modified *CIE L\\*a\\*b\\** colourspace formulae. Parameters ---------- XYZ Adapted *CIE XYZ* tristimulus values. Y_b Luminance factor of the background in :math:`cd/m^2`. F_S Surround induction factor :math:`F_S`. F_L Lightness induction factor :math:`F_L`. Returns ------- :class:`numpy.ndarray` Opponent colour dimensions. Examples -------- >>> XYZ = np.array([19.00999572, 20.00091862, 21.77993863]) >>> Y_b = 20.0 >>> F_S = 3.0 >>> F_L = 1.0 >>> opponent_colour_dimensions(XYZ, Y_b, F_S, F_L) # doctest: +ELLIPSIS array([ 3.7368047...e+01, -4.4986443...e-03, -5.2604647...e-03]) """ X, Y, Z = tsplit(XYZ) Y_b = as_float_array(Y_b) F_S = as_float_array(F_S) F_L = as_float_array(F_L) # Account for background lightness contrast. z = 1 + F_L * spow(Y_b / 100, 0.5) # Computing modified *CIE L\\*a\\*b\\** colourspace array. L = 116 * spow(f(Y / 100, F_S), z) - 16 a = 500 * (f(X / 95.05, F_S) - f(Y / 100, F_S)) b = 200 * (f(Y / 100, F_S) - f(Z / 108.88, F_S)) Lab = tstack([L, a, b]) return Lab
def RGB_c( x_1: NDArray, x_2: NDArray, y_1: NDArray, y_2: NDArray, z: NDArray, n: NDArray, ) -> NDArray: """Compute the corresponding colour cone responses component.""" return (Y_o * x_2 + n) * spow(K, 1 / y_2) * spow( (z + n) / (Y_o * x_1 + n), y_1 / y_2) - n
def adjusted_reference_white_signals( rgb_p: ArrayLike, rgb_b: ArrayLike, rgb_w: ArrayLike, p: FloatingOrArrayLike, ) -> NDArray: """ Adjust the white point for simultaneous chromatic contrast. Parameters ---------- rgb_p Cone signals *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array of the proximal field. rgb_b Cone signals *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array of the background. rgb_w Cone signals array *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array of the reference white. p Simultaneous contrast / assimilation factor :math:`p` with value normalised to domain [-1, 0] when simultaneous contrast occurs and normalised to domain [0, 1] when assimilation occurs. Returns ------- :class:`numpy.ndarray` Adjusted cone signals *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array of the reference white. Examples -------- >>> rgb_p = np.array([98.07193550, 101.13755950, 100.00000000]) >>> rgb_b = np.array([0.99984505, 0.99983840, 0.99982674]) >>> rgb_w = np.array([97.37325710, 101.54968030, 108.88000000]) >>> p = 0.1 >>> adjusted_reference_white_signals(rgb_p, rgb_b, rgb_w, p) ... # doctest: +ELLIPSIS array([ 88.0792742..., 91.8569553..., 98.4876543...]) """ rgb_p = as_float_array(rgb_p) rgb_b = as_float_array(rgb_b) rgb_w = as_float_array(rgb_w) p = as_float_array(p) p_rgb = rgb_p / rgb_b rgb_w = (rgb_w * (spow((1 - p) * p_rgb + (1 + p) / p_rgb, 0.5)) / (spow( (1 + p) * p_rgb + (1 - p) / p_rgb, 0.5))) return rgb_w
def chromatic_adaptation(RGB, RGB_0, RGB_0r, Y, D=1): """ Applies chromatic adaptation to given *RGB* normalised cone responses array. Parameters ---------- RGB : array_like *RGB* normalised cone responses array of test sample / stimulus. RGB_0 : array_like *RGB* normalised cone responses array of reference white. RGB_0r : array_like *RGB* normalised cone responses array of reference illuminant *CIE Standard Illuminant D Series* *D65*. Y : numeric or array_like Tristimulus values :math:`Y` of the stimulus. D : numeric or array_like, optional *Discounting-the-Illuminant* factor normalised to domain [0, 1]. Returns ------- ndarray Adapted *CIE XYZ* tristimulus values. Examples -------- >>> RGB = np.array([0.94142795, 1.04040120, 1.08970885]) >>> RGB_0 = np.array([0.94146023, 1.04039386, 1.08950293]) >>> RGB_0r = np.array([0.94146023, 1.04039386, 1.08950293]) >>> Y = 20.0 >>> chromatic_adaptation(RGB, RGB_0, RGB_0r, Y) # doctest: +ELLIPSIS array([ 19.01, 20. , 21.78]) """ R, G, B = tsplit(RGB) R_0, G_0, B_0 = tsplit(RGB_0) R_0r, G_0r, B_0r = tsplit(RGB_0r) Y = as_float_array(Y) beta = spow(B_0 / B_0r, 0.0834) R_r = (D * (R_0r / R_0) + 1 - D) * R G_r = (D * (G_0r / G_0) + 1 - D) * G B_r = (D * (B_0r / spow(B_0, beta)) + 1 - D) * spow(B, beta) RGB_r = tstack([R_r, G_r, B_r]) Y = tstack([Y, Y, Y]) XYZ_r = dot_vector(LLAB_RGB_TO_XYZ_MATRIX, RGB_r * Y) return XYZ_r
def opponent_colour_dimensions(XYZ, Y_b, F_S, F_L): """ Returns opponent colour dimensions from given adapted *CIE XYZ* tristimulus values. The opponent colour dimensions are based on a modified *CIE L\\*a\\*b\\** colourspace formulae. Parameters ---------- XYZ : array_like Adapted *CIE XYZ* tristimulus values. Y_b : numeric or array_like Luminance factor of the background in :math:`cd/m^2`. F_S : numeric or array_like Surround induction factor :math:`F_S`. F_L : numeric or array_like Lightness induction factor :math:`F_L`. Returns ------- ndarray Opponent colour dimensions. Examples -------- >>> XYZ = np.array([19.00999572, 20.00091862, 21.77993863]) >>> Y_b = 20.0 >>> F_S = 3.0 >>> F_L = 1.0 >>> opponent_colour_dimensions(XYZ, Y_b, F_S, F_L) # doctest: +ELLIPSIS array([ 3.7368047...e+01, -4.4986443...e-03, -5.2604647...e-03]) """ X, Y, Z = tsplit(XYZ) Y_b = as_float_array(Y_b) F_S = as_float_array(F_S) F_L = as_float_array(F_L) # Account for background lightness contrast. z = 1 + F_L * spow(Y_b / 100, 0.5) # Computing modified *CIE L\\*a\\*b\\** colourspace array. L = 116 * spow(f(Y / 100, F_S), z) - 16 a = 500 * (f(X / 95.05, F_S) - f(Y / 100, F_S)) b = 200 * (f(Y / 100, F_S) - f(Z / 108.88, F_S)) Lab = tstack([L, a, b]) return Lab
def chroma_correlate(J, n, N_c, N_cb, e_t, a, b, RGB_a): """ Returns the *chroma* correlate :math:`C`. Parameters ---------- J : numeric or array_like *Lightness* correlate :math:`J`. n : numeric or array_like Function of the luminance factor of the background :math:`n`. N_c : numeric or array_like Surround chromatic induction factor :math:`N_{c}`. N_cb : numeric or array_like Chromatic induction factor :math:`N_{cb}`. e_t : numeric or array_like Eccentricity factor :math:`e_t`. a : numeric or array_like Opponent colour dimension :math:`a`. b : numeric or array_like Opponent colour dimension :math:`b`. RGB_a : array_like Compressed stimulus *CMCCAT2000* transform sharpened *RGB* array. Returns ------- numeric or ndarray *Chroma* correlate :math:`C`. Examples -------- >>> J = 41.7310911325 >>> n = 0.2 >>> N_c = 1.0 >>> N_cb = 1.00030400456 >>> e_t = 1.17400547285 >>> a = -0.000624112068243 >>> b = -0.000506270106773 >>> RGB_a = np.array([7.94632020, 7.94711528, 7.94899595]) >>> chroma_correlate(J, n, N_c, N_cb, e_t, a, b, RGB_a) ... # doctest: +ELLIPSIS 0.1047077... """ J = as_float_array(J) n = as_float_array(n) t = temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a) C = spow(t, 0.9) * spow(J / 100, 0.5) * spow(1.64 - 0.29 ** n, 0.73) return C
def chroma_correlate( s: FloatingOrArrayLike, Y_b: FloatingOrArrayLike, Y_w: FloatingOrArrayLike, Q: FloatingOrArrayLike, Q_w: FloatingOrArrayLike, ) -> FloatingOrNDArray: """ Return the *chroma* correlate :math:`C_94`. Parameters ---------- s *Saturation* correlate :math:`s`. Y_b Tristimulus values :math:`Y_b` the background. Y_w Tristimulus values :math:`Y_b` the reference white. Q *Brightness* correlate :math:`Q` of the stimulus. Q_w *Brightness* correlate :math:`Q` of the reference white. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Chroma* correlate :math:`C_94`. Examples -------- >>> s = 0.0199093206929 >>> Y_b = 100.0 >>> Y_w = 100.0 >>> Q = 22.209765491265024 >>> Q_w = 40.518065821226081 >>> chroma_correlate(s, Y_b, Y_w, Q, Q_w) # doctest: +ELLIPSIS 0.1210508... """ s = as_float_array(s) Y_b = as_float_array(Y_b) Y_w = as_float_array(Y_w) Q = as_float_array(Q) Q_w = as_float_array(Q_w) C_94 = (2.44 * spow(s, 0.69) * (spow(Q / Q_w, Y_b / Y_w)) * (1.64 - spow(0.29, Y_b / Y_w))) return C_94
def chroma_correlate(a, b): """ Returns the correlate of *chroma* :math:`Ch_L`. Parameters ---------- a : numeric or array_like Opponent colour dimension :math:`a`. b : numeric or array_like Opponent colour dimension :math:`b`. Returns ------- numeric or ndarray Correlate of *chroma* :math:`Ch_L`. Examples -------- >>> a = -4.49864756e-03 >>> b = -5.26046353e-03 >>> chroma_correlate(a, b) # doctest: +ELLIPSIS 0.0086506... """ a = as_float_array(a) b = as_float_array(b) c = spow(a ** 2 + b ** 2, 0.5) Ch_L = 25 * np.log(1 + 0.05 * c) return Ch_L
def saturation_correlate(M, Q): """ Returns the *saturation* correlate :math:`s`. Parameters ---------- M : numeric or array_like *Colourfulness* correlate :math:`M`. Q : numeric or array_like *Brightness* correlate :math:`C`. Returns ------- numeric or ndarray *Saturation* correlate :math:`s`. Examples -------- >>> M = 0.108842175669 >>> Q = 195.371325966 >>> saturation_correlate(M, Q) # doctest: +ELLIPSIS 2.3603053... """ M = as_float_array(M) Q = as_float_array(Q) s = 100 * spow(M / Q, 0.5) return s
def luminance_to_retinal_illuminance(XYZ, Y_c): """ Converts from luminance in :math:`cd/m^2` to retinal illuminance in trolands. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Y_c : numeric or array_like Absolute adapting field luminance in :math:`cd/m^2`. Returns ------- ndarray Converted *CIE XYZ* tristimulus values in trolands. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> Y_0 = 318.31 >>> luminance_to_retinal_illuminance(XYZ, Y_0) # doctest: +ELLIPSIS array([ 479.4445924..., 499.3174313..., 534.5631673...]) """ XYZ = as_float_array(XYZ) Y_c = as_float_array(Y_c) return 18 * spow(Y_c[..., np.newaxis] * XYZ / 100, 0.8)
def chromatic_induction_factors(n): """ Returns the chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`. Parameters ---------- n : numeric or array_like Function of the luminance factor of the background :math:`n`. Returns ------- ndarray Chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`. Examples -------- >>> chromatic_induction_factors(0.2) # doctest: +ELLIPSIS array([ 1.000304, 1.000304]) """ n = as_float_array(n) N_bb = N_cb = 0.725 * spow(1 / n, 0.2) N_bbcb = tstack([N_bb, N_cb]) return N_bbcb
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...]) """ LMS = vector_dot([ [0.2435, 0.8524, -0.0516], [-0.3954, 1.1642, 0.0837], [0.0000, 0.0400, 0.6225], ], XYZ) LMS *= np.array([0.66, 1.0, 0.43]) LMS = spow(LMS, 0.7) LMS += np.array([0.024, 0.036, 0.31]) return LMS
def colourfulness_correlate(C, F_L): """ Returns the *colourfulness* correlate :math:`M`. Parameters ---------- C : numeric or array_like *Chroma* correlate :math:`C`. F_L : numeric or array_like *Luminance* level adaptation factor :math:`F_L`. Returns ------- numeric or ndarray *Colourfulness* correlate :math:`M`. Examples -------- >>> C = 0.104707757171 >>> F_L = 1.16754446415 >>> colourfulness_correlate(C, F_L) # doctest: +ELLIPSIS 0.1088421... """ C = as_float_array(C) F_L = as_float_array(F_L) M = C * spow(F_L, 0.25) return M
def post_adaptation_non_linear_response_compression_reverse(RGB, F_L): """ Returns given *CMCCAT2000* transform sharpened *RGB* array without post adaptation non linear response compression. Parameters ---------- RGB : array_like *CMCCAT2000* transform sharpened *RGB* array. F_L : array_like *Luminance* level adaptation factor :math:`F_L`. Returns ------- ndarray Uncompressed *CMCCAT2000* transform sharpened *RGB* array. Examples -------- >>> RGB = np.array([7.94632020, 7.94711528, 7.94899595]) >>> F_L = 1.16754446415 >>> post_adaptation_non_linear_response_compression_reverse(RGB, F_L) ... # doctest: +ELLIPSIS array([ 19.9969397..., 20.0018612..., 20.0135052...]) """ RGB = as_float_array(RGB) F_L = as_float_array(F_L) RGB_p = ((np.sign(RGB - 0.1) * (100 / F_L[..., np.newaxis]) * spow( (27.13 * np.absolute(RGB - 0.1)) / (400 - np.absolute(RGB - 0.1)), 1 / 0.42))) return RGB_p
def chroma_correlate(L_star_P, S): """ Returns the correlate of *chroma* :math:`C`. Parameters ---------- L_star_P : numeric or array_like *Achromatic Lightness* correlate :math:`L_p^\\star`. S : numeric or array_like Correlate of *saturation* :math:`S`. Returns ------- numeric or ndarray Correlate of *chroma* :math:`C`. Examples -------- >>> L_star_P = 49.99988297570504 >>> S = 0.013355029751778 >>> chroma_correlate(L_star_P, S) # doctest: +ELLIPSIS 0.0133550... """ L_star_P = as_float_array(L_star_P) S = as_float_array(S) C = spow(L_star_P / 50, 0.7) * S return C
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...]) """ LMS = dot_vector([ [0.2435, 0.8524, -0.0516], [-0.3954, 1.1642, 0.0837], [0.0000, 0.0400, 0.6225], ], XYZ) LMS *= np.array([0.66, 1.0, 0.43]) LMS = spow(LMS, 0.7) LMS += np.array([0.024, 0.036, 0.31]) return LMS
def saturation_correlate(M: FloatingOrArrayLike, Q: FloatingOrArrayLike) -> FloatingOrNDArray: """ Return the *saturation* correlate :math:`s`. Parameters ---------- M *Colourfulness* correlate :math:`M`. Q *Brightness* correlate :math:`C`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Saturation* correlate :math:`s`. Examples -------- >>> M = 0.108842175669 >>> Q = 195.371325966 >>> saturation_correlate(M, Q) # doctest: +ELLIPSIS 2.3603053... """ M = as_float_array(M) Q = as_float_array(Q) s = 100 * spow(M / Q, 0.5) return s
def lightness_Fairchild2011(Y, epsilon=0.474, method='hdr-CIELAB'): """ Computes *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using *Fairchild and Chen (2011)* method according to *Michealis-Menten* kinetics. Parameters ---------- Y : array_like *luminance* :math:`Y`. epsilon : numeric or array_like, optional :math:`\\epsilon` exponent. method : unicode, optional **{'hdr-CIELAB', 'hdr-IPT'}**, *Lightness* :math:`L_{hdr}` computation method. Returns ------- array_like *Lightness* :math:`L_{hdr}`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_hdr`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Fairchild2011` Examples -------- >>> lightness_Fairchild2011(12.19722535 / 100) # doctest: +ELLIPSIS 51.8529584... >>> lightness_Fairchild2011(12.19722535 / 100, method='hdr-IPT') ... # doctest: +ELLIPSIS 51.6431084... """ Y = to_domain_1(Y) if method.lower() == 'hdr-cielab': maximum_perception = 247 else: maximum_perception = 246 L_hdr = reaction_rate_MichealisMenten( spow(Y, epsilon), maximum_perception, 2 ** epsilon) + 0.02 return from_range_100(L_hdr)
def intermediate_lightness_function_CIE1976(Y, Y_n=100): """ Returns the intermediate value :math:`f(Y/Yn)` in the *Lightness* :math:`L^*` computation for given *luminance* :math:`Y` using given reference white *luminance* :math:`Y_n` as per *CIE 1976* recommendation. Parameters ---------- Y : numeric or array_like *luminance* :math:`Y`. Y_n : numeric or array_like, optional White reference *luminance* :math:`Y_n`. Returns ------- numeric or array_like Intermediate value :math:`f(Y/Yn)`. Notes ----- +-------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+===============+ | ``Y`` | [0, 100] | [0, 100] | +-------------+-----------------------+---------------+ +-------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +=============+=======================+===============+ | ``f_Y_Y_n`` | [0, 1] | [0, 1] | +-------------+-----------------------+---------------+ References ---------- :cite:`CIETC1-482004m`, :cite:`Wyszecki2000bd` Examples -------- >>> intermediate_lightness_function_CIE1976(12.19722535) ... # doctest: +ELLIPSIS 0.4959299... >>> intermediate_lightness_function_CIE1976(12.19722535, 95) ... # doctest: +ELLIPSIS 0.5044821... """ Y = as_float_array(Y) Y_n = as_float_array(Y_n) Y_Y_n = Y / Y_n f_Y_Y_n = as_float( np.where( Y_Y_n > (24 / 116) ** 3, spow(Y_Y_n, 1 / 3), (841 / 108) * Y_Y_n + 16 / 116, )) return f_Y_Y_n
def colourfulness_correlate(F_L, C_94): """ Returns the *colourfulness* correlate :math:`M_94`. Parameters ---------- F_L : numeric or array_like Luminance adaptation factor :math:`F_L`. C_94 : numeric *Chroma* correlate :math:`C_94`. Returns ------- numeric *Colourfulness* correlate :math:`M_94`. Examples -------- >>> F_L = 1.16754446414718 >>> C_94 = 0.121050839936176 >>> colourfulness_correlate(F_L, C_94) # doctest: +ELLIPSIS 0.1238964... """ F_L = as_float_array(F_L) C_94 = as_float_array(C_94) M_94 = spow(F_L, 0.15) * C_94 return M_94
def post_adaptation_non_linear_response_compression_reverse(RGB, F_L): """ Returns given *CMCCAT2000* transform sharpened *RGB* array without post adaptation non linear response compression. Parameters ---------- RGB : array_like *CMCCAT2000* transform sharpened *RGB* array. F_L : array_like *Luminance* level adaptation factor :math:`F_L`. Returns ------- ndarray Uncompressed *CMCCAT2000* transform sharpened *RGB* array. Examples -------- >>> RGB = np.array([7.94632020, 7.94711528, 7.94899595]) >>> F_L = 1.16754446415 >>> post_adaptation_non_linear_response_compression_reverse(RGB, F_L) ... # doctest: +ELLIPSIS array([ 19.9969397..., 20.0018612..., 20.0135052...]) """ RGB = as_float_array(RGB) F_L = as_float_array(F_L) RGB_p = (((100 / F_L[..., np.newaxis]) * spow( (27.13 * (RGB - 0.1)) / (400 - (RGB - 0.1)), 1 / 0.42))) return RGB_p
def colourfulness_correlate(C: FloatingOrArrayLike, F_L: FloatingOrArrayLike) -> FloatingOrNDArray: """ Return the *colourfulness* correlate :math:`M`. Parameters ---------- C *Chroma* correlate :math:`C`. F_L *Luminance* level adaptation factor :math:`F_L`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Colourfulness* correlate :math:`M`. Examples -------- >>> C = 0.104707757171 >>> F_L = 1.16754446415 >>> colourfulness_correlate(C, F_L) # doctest: +ELLIPSIS 0.1088421... """ C = as_float_array(C) F_L = as_float_array(F_L) M = C * spow(F_L, 0.25) return M
def overall_chromatic_response(M_yb, M_rg): """ Returns the overall chromatic response :math:`M`. Parameters ---------- M_yb : numeric or array_like Yellowness / blueness response :math:`M_{yb}`. M_rg : numeric or array_like Redness / greenness response :math:`M_{rg}`. Returns ------- numeric or ndarray Overall chromatic response :math:`M`. Examples -------- >>> M_yb = -0.008237223618825 >>> M_rg = -0.000104447583276 >>> overall_chromatic_response(M_yb, M_rg) # doctest: +ELLIPSIS 0.0082378... """ M_yb = as_float_array(M_yb) M_rg = as_float_array(M_rg) M = spow((M_yb**2) + (M_rg**2), 0.5) return M
def f_n(x): """ Defines the nonlinear response function of the *Hunt* colour appearance model used to model the nonlinear behaviour of various visual responses. Parameters ---------- x : numeric or array_like or array_like Visual response variable :math:`x`. Returns ------- numeric or array_like Modeled visual response variable :math:`x`. Examples -------- >>> x = np.array([0.23350512, 0.23351103, 0.23355179]) >>> f_n(x) # doctest: +ELLIPSIS array([ 5.8968592..., 5.8969521..., 5.8975927...]) """ x = as_float_array(x) x_p = spow(x, 0.73) x_m = 40 * (x_p / (x_p + 2)) return x_m
def illuminant_scotopic_luminance(L_A, CCT): """ Returns the approximate scotopic luminance :math:`L_{AS}` of the illuminant. Parameters ---------- L_A : numeric or array_like Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. CCT : numeric or array_like Correlated color temperature :math:`T_{cp}` of the illuminant. Returns ------- numeric or ndarray Approximate scotopic luminance :math:`L_{AS}`. Examples -------- >>> illuminant_scotopic_luminance(318.31, 6504.0) # doctest: +ELLIPSIS 769.9376286... """ L_A = as_float_array(L_A) CCT = as_float_array(CCT) CCT = 2.26 * L_A * spow((CCT / 4000) - 0.4, 1 / 3) return CCT
def luminance_level_adaptation_factor(L_A): """ Returns the *luminance* level adaptation factor :math:`F_L`. Parameters ---------- L_A : numeric or array_like Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. Returns ------- numeric or ndarray *Luminance* level adaptation factor :math:`F_L` Examples -------- >>> luminance_level_adaptation_factor(318.31) # doctest: +ELLIPSIS 1.1675444... """ L_A = as_float_array(L_A) k = 1 / (5 * L_A + 1) k4 = k**4 F_L = 0.2 * k4 * (5 * L_A) + 0.1 * (1 - k4)**2 * spow(5 * L_A, 1 / 3) return F_L
def chroma_correlate(L_star_P: FloatingOrArrayLike, S: FloatingOrArrayLike) -> FloatingOrNDArray: """ Return the correlate of *chroma* :math:`C`. Parameters ---------- L_star_P *Achromatic Lightness* correlate :math:`L_p^\\star`. S Correlate of *saturation* :math:`S`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Correlate of *chroma* :math:`C`. Examples -------- >>> L_star_P = 49.99988297570504 >>> S = 0.013355029751778 >>> chroma_correlate(L_star_P, S) # doctest: +ELLIPSIS 0.0133550... """ L_star_P = as_float_array(L_star_P) S = as_float_array(S) C = spow(L_star_P / 50, 0.7) * S return C
def chromatic_induction_factors(n: FloatingOrArrayLike) -> NDArray: """ Return the chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`. Parameters ---------- n Function of the luminance factor of the background :math:`n`. Returns ------- :class:`numpy.ndarray` Chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`. Examples -------- >>> chromatic_induction_factors(0.2) # doctest: +ELLIPSIS array([ 1.000304, 1.000304]) """ n = as_float_array(n) N_bb = N_cb = as_float(0.725) * spow(1 / n, 0.2) N_bbcb = tstack([N_bb, N_cb]) return N_bbcb
def lightness_Fairchild2010(Y, epsilon=1.836): """ Computes *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using *Fairchild and Wyble (2010)* method according to *Michealis-Menten* kinetics. Parameters ---------- Y : array_like *luminance* :math:`Y`. epsilon : numeric or array_like, optional :math:`\\epsilon` exponent. Returns ------- array_like *Lightness* :math:`L_{hdr}`. Warning ------- The input domain of that definition is non standard! Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_hdr`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Fairchild2010` Examples -------- >>> lightness_Fairchild2010(12.19722535 / 100) # doctest: +ELLIPSIS 31.9963902... """ Y = to_domain_1(Y) maximum_perception = 100 L_hdr = reaction_rate_MichealisMenten( spow(Y, epsilon), maximum_perception, 0.184 ** epsilon) + 0.02 return from_range_100(L_hdr)
def eotf_reverse_DCDM(XYZ, out_int=False): """ Defines the *DCDM* reverse electro-optical transfer function (EOTF / EOCF). Parameters ---------- XYZ : numeric or array_like *CIE XYZ* tristimulus values. out_int : bool, optional Whether to return value as integer code value or float equivalent of a code value at a given bit depth. Returns ------- numeric or ndarray Non-linear *CIE XYZ'* tristimulus values. Notes ----- +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+---------------+ +----------------+-----------------------+---------------+ | **Range \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ | ``XYZ_p`` | [0, 1] | [0, 1] | +----------------+-----------------------+---------------+ - \\* This definition has an output integer switch, thus the domain-range scale information is only given for the floating point mode. References ---------- :cite:`DigitalCinemaInitiatives2007b` Examples -------- >>> eotf_reverse_DCDM(0.18) # doctest: +ELLIPSIS 0.1128186... >>> eotf_reverse_DCDM(0.18, out_int=True) 462 """ XYZ = to_domain_1(XYZ) XYZ_p = spow(XYZ / 52.37, 1 / 2.6) if out_int: return np.round(4095 * XYZ_p).astype(DEFAULT_INT_DTYPE) else: return from_range_1(XYZ_p)
def eotf_DCDM(XYZ_p, in_int=False): """ Defines the *DCDM* electro-optical transfer function (EOTF / EOCF). Parameters ---------- XYZ_p : numeric or array_like Non-linear *CIE XYZ'* tristimulus values. in_int : bool, optional Whether to treat the input value as integer code value or float equivalent of a code value at a given bit depth. Returns ------- numeric or ndarray *CIE XYZ* tristimulus values. Notes ----- +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ | ``XYZ_p`` | [0, 1] | [0, 1] | +----------------+-----------------------+---------------+ +----------------+-----------------------+---------------+ | **Range \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+---------------+ - \\* This definition has an input integer switch, thus the domain-range scale information is only given for the floating point mode. References ---------- :cite:`DigitalCinemaInitiatives2007b` Examples -------- >>> eotf_DCDM(0.11281860951766724) # doctest: +ELLIPSIS 0.18... >>> eotf_DCDM(462, in_int=True) # doctest: +ELLIPSIS 0.18... """ XYZ_p = to_domain_1(XYZ_p) if in_int: XYZ_p = XYZ_p / 4095 XYZ = 52.37 * spow(XYZ_p, 2.6) return from_range_1(XYZ)
def XYZ_to_IPT(XYZ): """ Converts from *CIE XYZ* tristimulus values to *IPT* colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray *IPT* colourspace array. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``IPT`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``P`` : [-1, 1] | ``P`` : [-1, 1] | | | | | | | ``T`` : [-1, 1] | ``T`` : [-1, 1] | +------------+-----------------------+-----------------+ - Input *CIE XYZ* tristimulus values needs to be adapted for *CIE Standard Illuminant D Series* *D65*. References ---------- :cite:`Fairchild2013y` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_IPT(XYZ) # doctest: +ELLIPSIS array([ 0.3842619..., 0.3848730..., 0.1888683...]) """ XYZ = to_domain_1(XYZ) LMS = dot_vector(IPT_XYZ_TO_LMS_MATRIX, XYZ) LMS_prime = spow(LMS, 0.43) IPT = dot_vector(IPT_LMS_TO_IPT_MATRIX, LMS_prime) return from_range_1(IPT)
def temporary_magnitude_quantity_reverse(C, J, n): """ Returns the temporary magnitude quantity :math:`t`. for reverse *CIECAM02* implementation. Parameters ---------- C : numeric or array_like *Chroma* correlate :math:`C`. J : numeric or array_like *Lightness* correlate :math:`J`. n : numeric or array_like Function of the luminance factor of the background :math:`n`. Returns ------- numeric or ndarray Temporary magnitude quantity :math:`t`. Notes ----- - This definition implements negative values handling as per :cite:`Luo2013`. Examples -------- >>> C = 68.8364136888275 >>> J = 41.749268505999 >>> n = 0.2 >>> temporary_magnitude_quantity_reverse(C, J, n) # doctest: +ELLIPSIS 202.3873619... """ C = as_float_array(C) J = np.maximum(J, EPSILON) n = as_float_array(n) t = spow(C / (np.sqrt(J / 100) * spow(1.64 - 0.29 ** n, 0.73)), 1 / 0.9) return t
def beta_1(x): """ Computes the exponent :math:`\\beta_1` for the middle and long-wavelength sensitive cones. Parameters ---------- x: numeric or array_like Middle and long-wavelength sensitive cone response. Returns ------- numeric or array_like Exponent :math:`\\beta_1`. Examples -------- >>> beta_1(318.323316315) # doctest: +ELLIPSIS 4.6106222... """ return (6.469 + 6.362 * spow(x, 0.4495)) / (6.469 + spow(x, 0.4495))
def oetf_reverse_sRGB(V): """ Defines the *sRGB* colourspace reverse opto-electronic transfer function (OETF / OECF). Parameters ---------- V : numeric or array_like Electrical signal :math:`V`. Returns ------- numeric or ndarray Corresponding *luminance* :math:`L` of the image. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``V`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`InternationalElectrotechnicalCommission1999a`, :cite:`InternationalTelecommunicationUnion2015i` Examples -------- >>> oetf_reverse_sRGB(0.461356129500442) # doctest: +ELLIPSIS 0.1... """ V = to_domain_1(V) with domain_range_scale('ignore'): L = np.where( V <= oetf_sRGB(0.0031308), V / 12.92, spow((V + 0.055) / 1.055, 2.4), ) return as_float(from_range_1(L))
def IPT_to_XYZ(IPT): """ Converts from *IPT* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- IPT : array_like *IPT* colourspace array. Returns ------- ndarray *CIE XYZ* tristimulus values. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``IPT`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``P`` : [-1, 1] | ``P`` : [-1, 1] | | | | | | | ``T`` : [-1, 1] | ``T`` : [-1, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`Fairchild2013y` Examples -------- >>> IPT = np.array([0.38426191, 0.38487306, 0.18886838]) >>> IPT_to_XYZ(IPT) # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) """ IPT = to_domain_1(IPT) LMS = dot_vector(IPT_IPT_TO_LMS_MATRIX, IPT) LMS_prime = spow(LMS, 1 / 0.43) XYZ = dot_vector(IPT_LMS_TO_XYZ_MATRIX, LMS_prime) return from_range_1(XYZ)
def eotf_SMPTE240M(V_r): """ Defines *SMPTE 240M* electro-optical transfer function (EOTF / EOCF). Parameters ---------- V_r : numeric or array_like Video signal level :math:`V_r` driving the reference reproducer normalised to the system reference white. Returns ------- numeric or ndarray Light output :math:`L_r` from the reference reproducer normalised to the system reference white. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``V_c`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_c`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`SocietyofMotionPictureandTelevisionEngineers1999b` Examples -------- >>> eotf_SMPTE240M(0.402285796753870) # doctest: +ELLIPSIS 0.1... """ V_r = to_domain_1(V_r) with domain_range_scale('ignore'): L_r = np.where( V_r < oetf_SMPTE240M(0.0228), V_r / 4, spow((V_r + 0.1115) / 1.1115, 1 / 0.45), ) return as_float(from_range_1(L_r))
def beta_2(x): """ Computes the exponent :math:`\\beta_2` for the short-wavelength sensitive cones. Parameters ---------- x: numeric or array_like Short-wavelength sensitive cone response. Returns ------- numeric or array_like Exponent :math:`\\beta_2`. Examples -------- >>> beta_2(318.323316315) # doctest: +ELLIPSIS 4.6522416... """ return 0.7844 * (8.414 + 8.091 * spow(x, 0.5128)) / ( 8.414 + spow(x, 0.5128))
def lightness_Wyszecki1963(Y): """ Returns the *Lightness* :math:`W` of given *luminance* :math:`Y` using *Wyszecki (1963)* method. Parameters ---------- Y : numeric or array_like *luminance* :math:`Y`. Returns ------- numeric or array_like *Lightness* :math:`W`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``W`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Wyszecki1963b` Examples -------- >>> lightness_Wyszecki1963(12.19722535) # doctest: +ELLIPSIS 40.5475745... """ Y = to_domain_100(Y) if np.any(Y < 1) or np.any(Y > 98): usage_warning('"W*" Lightness computation is only applicable for ' '1% < "Y" < 98%, unpredictable results may occur!') W = 25 * spow(Y, 1 / 3) - 17 return from_range_100(W)
def temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a): """ Returns the temporary magnitude quantity :math:`t`. for forward *CIECAM02* implementation. Parameters ---------- N_c : numeric or array_like Surround chromatic induction factor :math:`N_{c}`. N_cb : numeric or array_like Chromatic induction factor :math:`N_{cb}`. e_t : numeric or array_like Eccentricity factor :math:`e_t`. a : numeric or array_like Opponent colour dimension :math:`a`. b : numeric or array_like Opponent colour dimension :math:`b`. RGB_a : array_like Compressed stimulus *CMCCAT2000* transform sharpened *RGB* array. Returns ------- numeric or ndarray Temporary magnitude quantity :math:`t`. Examples -------- >>> N_c = 1.0 >>> N_cb = 1.00030400456 >>> e_t = 1.174005472851914 >>> a = -0.000624112068243 >>> b = -0.000506270106773 >>> RGB_a = np.array([7.94632020, 7.94711528, 7.94899595]) >>> temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a) ... # doctest: +ELLIPSIS 0.1497462... """ N_c = as_float_array(N_c) N_cb = as_float_array(N_cb) e_t = as_float_array(e_t) a = as_float_array(a) b = as_float_array(b) Ra, Ga, Ba = tsplit(RGB_a) t = (((50000 / 13) * N_c * N_cb) * (e_t * spow(a ** 2 + b ** 2, 0.5)) / (Ra + Ga + 21 * Ba / 20)) return t
def oetf_sRGB(L): """ Defines the *sRGB* colourspace opto-electronic transfer function (OETF / OECF). Parameters ---------- L : numeric or array_like *Luminance* :math:`L` of the image. Returns ------- numeric or ndarray Corresponding electrical signal :math:`V`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``V`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`InternationalElectrotechnicalCommission1999a`, :cite:`InternationalTelecommunicationUnion2015i` Examples -------- >>> oetf_sRGB(0.18) # doctest: +ELLIPSIS 0.4613561... """ L = to_domain_1(L) V = np.where(L <= 0.0031308, L * 12.92, 1.055 * spow(L, 1 / 2.4) - 0.055) return as_float(from_range_1(V))