def test_from_range_100(self): """Test :func:`colour.utilities.common.from_range_100` definition.""" with domain_range_scale("Reference"): self.assertEqual(from_range_100(1), 1) with domain_range_scale("1"): self.assertEqual(from_range_100(1), 0.01) with domain_range_scale("100"): self.assertEqual(from_range_100(1), 1) with domain_range_scale("1"): self.assertEqual(from_range_100(1, np.pi), 1 / np.pi)
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 test_from_range_100(self): """ Tests :func:`colour.utilities.common.from_range_100` definition. """ with domain_range_scale('Reference'): self.assertEqual(from_range_100(1), 1) with domain_range_scale('1'): self.assertEqual(from_range_100(1), 0.01) with domain_range_scale('100'): self.assertEqual(from_range_100(1), 1) with domain_range_scale('1'): self.assertEqual(from_range_100(1, np.pi), 1 / np.pi)
def fn_b(a): """Change the domain-range scale for unit testing.""" b = to_domain_10(a) b *= 2 return from_range_100(b)
def yellowness_ASTME313( XYZ: ArrayLike, C_XZ: ArrayLike = YELLOWNESS_COEFFICIENTS_ASTME313[ "CIE 1931 2 Degree Standard Observer"]["D65"], ) -> FloatingOrNDArray: """ Return the *yellowness* index :math:`YI` of given sample *CIE XYZ* tristimulus values using *ASTM E313* method. ASTM E313 has successfully been used for a variety of white or near white materials. This includes coatings, plastics, textiles. Parameters ---------- XYZ *CIE XYZ* tristimulus values of the sample. C_XZ Coefficients :math:`C_X` and :math:`C_Z` for the *CIE 1931 2 Degree Standard Observer* and *CIE 1964 10 Degree Standard Observer* and *CIE Illuminant C* and *CIE Standard Illuminant D65*. Returns ------- :class:`np.floating` or :class:`numpy.ndarray` *Yellowness* :math:`YI`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``YI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`ASTMInternational2015` Examples -------- >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> yellowness_ASTME313(XYZ) # doctest: +ELLIPSIS 4.3400000... """ X, Y, Z = tsplit(to_domain_100(XYZ)) C_X, C_Z = tsplit(C_XZ) WI = 100 * (C_X * X - C_Z * Z) / Y return as_float(from_range_100(WI))
def whiteness_Berger1959(XYZ, XYZ_0): """ Returns the *whiteness* index :math:`WI` of given sample *CIE XYZ* tristimulus values using *Berger (1959)* method. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of sample. XYZ_0 : array_like *CIE XYZ* tristimulus values of reference white. Returns ------- numeric or ndarray *Whiteness* :math:`WI`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_0`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``WI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ - *Whiteness* :math:`WI` values larger than 33.33 indicate a bluish white and values smaller than 33.33 indicate a yellowish white. References ---------- :cite:`X-Rite2012a` Examples -------- >>> import numpy as np >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595]) >>> whiteness_Berger1959(XYZ, XYZ_0) # doctest: +ELLIPSIS 30.3638017... """ X, Y, Z = tsplit(to_domain_100(XYZ)) X_0, _Y_0, Z_0 = tsplit(to_domain_100(XYZ_0)) WI = 0.333 * Y + 125 * (Z / Z_0) - 125 * (X / X_0) return from_range_100(WI)
def lightness_CIE1976(Y, Y_n=100): """ Returns the *Lightness* :math:`L^*` of 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 *Lightness* :math:`L^*`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_star`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Lindbloom2003d`, :cite:`Wyszecki2000bd` Examples -------- >>> lightness_CIE1976(12.19722535) # doctest: +ELLIPSIS 41.5278758... """ Y = to_domain_100(Y) Y_n = as_float_array(Y_n) L_star = Y / Y_n L_star = as_float( np.where( L_star <= CIE_E, CIE_K * L_star, 116 * spow(L_star, 1 / 3) - 16, )) return from_range_100(L_star)
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 yellowness_ASTME313_alternative(XYZ: ArrayLike) -> FloatingOrNDArray: """ Return the *yellowness* index :math:`YI` of given sample *CIE XYZ* tristimulus values using the alternative *ASTM E313* method. In the original form of *Test Method E313*, an alternative equation was recommended for a *yellowness* index. In terms of colorimeter readings, it was :math:`YI = 100(1 − B/G)` where :math:`B` and :math:`G` are, respectively, blue and green colorimeter readings. Its derivation assumed that, because of the limitation of the concept to yellow (or blue) colors, it was not necessary to take account of variations in the amber or red colorimeter reading :math:`A`. This equation is no longer recommended. Parameters ---------- XYZ *CIE XYZ* tristimulus values of the sample. Returns ------- :class:`np.floating` or :class:`numpy.ndarray` *Yellowness* :math:`YI`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``YI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ - Input *CIE XYZ* tristimulus values must be adapted to *CIE Illuminant C*. References ---------- :cite:`ASTMInternational2015`, :cite:`X-Rite2012a` Examples -------- >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> yellowness_ASTME313_alternative(XYZ) # doctest: +ELLIPSIS 11.0650000... """ _X, Y, Z = tsplit(to_domain_100(XYZ)) WI = 100 * (1 - (0.847 * Z) / Y) return as_float(from_range_100(WI))
def _domain_range_change(a): """ Helper definition performing domain-range scale. """ b = to_domain_10(a) b *= 2 return from_range_100(b)
def fn_b(a): """ Helper definition performing domain-range scale. """ b = to_domain_10(a) b *= 2 return from_range_100(b)
def luminance_CIE1976(L_star, Y_n=100): """ Returns the *luminance* :math:`Y` of given *Lightness* :math:`L^*` with given reference white *luminance* :math:`Y_n`. Parameters ---------- L_star : numeric or array_like *Lightness* :math:`L^*` Y_n : numeric or array_like White reference *luminance* :math:`Y_n`. Returns ------- numeric or array_like *luminance* :math:`Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_star`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Lindbloom2003d`, :cite:`Wyszecki2000bd` Examples -------- >>> luminance_CIE1976(41.527875844653451) # doctest: +ELLIPSIS 12.1972253... >>> luminance_CIE1976(41.527875844653451, 95) # doctest: +ELLIPSIS 11.5873640... """ L_star = to_domain_100(L_star) Y_n = as_float_array(Y_n) Y = as_float( np.where( L_star > CIE_K * CIE_E, Y_n * ((L_star + 16) / 116)**3, Y_n * (L_star / CIE_K), )) return from_range_100(Y)
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 whiteness_Taube1960(XYZ: ArrayLike, XYZ_0: ArrayLike) -> FloatingOrNDArray: """ Return the *whiteness* index :math:`WI` of given sample *CIE XYZ* tristimulus values using *Taube (1960)* method. Parameters ---------- XYZ *CIE XYZ* tristimulus values of the sample. XYZ_0 *CIE XYZ* tristimulus values of the reference white. Returns ------- :class:`np.floating` or :class:`numpy.ndarray` *Whiteness* :math:`WI`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_0`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``WI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ - *Whiteness* :math:`WI` values larger than 100 indicate a bluish white and values smaller than 100 indicate a yellowish white. References ---------- :cite:`X-Rite2012a` Examples -------- >>> import numpy as np >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595]) >>> whiteness_Taube1960(XYZ, XYZ_0) # doctest: +ELLIPSIS 91.4071738... """ _X, Y, Z = tsplit(to_domain_100(XYZ)) _X_0, _Y_0, Z_0 = tsplit(to_domain_100(XYZ_0)) WI = 400 * (Z / Z_0) - 3 * Y return as_float(from_range_100(WI))
def yellowness_ASTMD1925(XYZ: ArrayLike) -> FloatingOrNDArray: """ Return the *yellowness* index :math:`YI` of given sample *CIE XYZ* tristimulus values using *ASTM D1925* method. ASTM D1925 has been specifically developed for the definition of the yellowness of homogeneous, non-fluorescent, almost neutral-transparent, white-scattering or opaque plastics as they will be reviewed under daylight condition. It can be other materials as well, as long as they fit into this description. Parameters ---------- XYZ *CIE XYZ* tristimulus values of the sample. Returns ------- :class:`np.floating` or :class:`numpy.ndarray` *Yellowness* :math:`YI`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``YI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ - Input *CIE XYZ* tristimulus values must be adapted to *CIE Illuminant C*. References ---------- :cite:`ASTMInternational2015`, :cite:`X-Rite2012a` Examples -------- >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> yellowness_ASTMD1925(XYZ) # doctest: +ELLIPSIS 10.2999999... """ X, Y, Z = tsplit(to_domain_100(XYZ)) YI = (100 * (1.28 * X - 1.06 * Z)) / Y return as_float(from_range_100(YI))
def whiteness_Stensby1968(Lab): """ Returns the *whiteness* index :math:`WI` of given sample *CIE L\\*a\\*b\\** colourspace array using *Stensby (1968)* method. Parameters ---------- Lab : array_like *CIE L\\*a\\*b\\** colourspace array of sample. Returns ------- numeric or ndarray *Whiteness* :math:`WI`. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``Lab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``a`` : [-100, 100] | ``a`` : [-1, 1] | | | | | | | ``b`` : [-100, 100] | ``b`` : [-1, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``WI`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ - *Whiteness* :math:`WI` values larger than 100 indicate a bluish white and values smaller than 100 indicate a yellowish white. References ---------- :cite:`X-Rite2012a` Examples -------- >>> import numpy as np >>> Lab = np.array([100.00000000, -2.46875131, -16.72486654]) >>> whiteness_Stensby1968(Lab) # doctest: +ELLIPSIS 142.7683456... """ L, a, b = tsplit(to_domain_100(Lab)) WI = L - 3 * b + 3 * a return from_range_100(WI)
def yellowness_ASTMD1925(XYZ): """ Returns the *yellowness* index :math:`YI` of given sample *CIE XYZ* tristimulus values using *ASTM D1925* method. ASTM D1925 has been specifically developed for the definition of the Yellowness of homogeneous, non-fluorescent, almost neutral-transparent, white-scattering or opaque plastics as they will be reviewed under daylight condition. It can be other materials as well, as long as they fit into this description. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of sample. Returns ------- numeric or ndarray *Whiteness* :math:`YI`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``YI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`X-Rite2012a` Examples -------- >>> import numpy as np >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> yellowness_ASTMD1925(XYZ) # doctest: +ELLIPSIS 10.2999999... """ X, Y, Z = tsplit(to_domain_100(XYZ)) YI = (100 * (1.28 * X - 1.06 * Z)) / Y return from_range_100(YI)
def luminance_CIE1976(L_star, Y_n=100): """ Returns the *luminance* :math:`Y` of given *Lightness* :math:`L^*` with given reference white *luminance* :math:`Y_n`. Parameters ---------- L_star : numeric or array_like *Lightness* :math:`L^*` Y_n : numeric or array_like White reference *luminance* :math:`Y_n`. Returns ------- numeric or array_like *luminance* :math:`Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_star`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`CIETC1-482004m`, :cite:`Wyszecki2000bd` Examples -------- >>> luminance_CIE1976(41.527875844653451) # doctest: +ELLIPSIS 12.1972253... >>> luminance_CIE1976(41.527875844653451, 95) # doctest: +ELLIPSIS 11.5873640... """ L_star = to_domain_100(L_star) Y_n = as_float_array(Y_n) f_Y_Y_n = (L_star + 16) / 116 Y = intermediate_luminance_function_CIE1976(f_Y_Y_n, Y_n) return from_range_100(Y)
def luminance_CIE1976(L_star: FloatingOrArrayLike, Y_n: FloatingOrArrayLike = 100) -> FloatingOrNDArray: """ Return the *luminance* :math:`Y` of given *Lightness* :math:`L^*` with given reference white *luminance* :math:`Y_n`. Parameters ---------- L_star *Lightness* :math:`L^*` Y_n White reference *luminance* :math:`Y_n`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Luminance* :math:`Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_star`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`CIETC1-482004m`, :cite:`Wyszecki2000bd` Examples -------- >>> luminance_CIE1976(41.527875844653451) # doctest: +ELLIPSIS 12.1972253... >>> luminance_CIE1976(41.527875844653451, 95) # doctest: +ELLIPSIS 11.5873640... """ L_star = to_domain_100(L_star) Y_n = as_float_array(Y_n) f_Y_Y_n = (L_star + 16) / 116 Y = intermediate_luminance_function_CIE1976(f_Y_Y_n, Y_n) return as_float(from_range_100(Y))
def lightness_Wyszecki1963(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ Return the *Lightness* :math:`W` of given *luminance* :math:`Y` using *Wyszecki (1963)* method. Parameters ---------- Y *Luminance* :math:`Y`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *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 as_float(from_range_100(W))
def lightness_CIE1976( Y: FloatingOrArrayLike, Y_n: FloatingOrArrayLike = 100 ) -> FloatingOrNDArray: """ Return the *Lightness* :math:`L^*` of given *luminance* :math:`Y` using given reference white *luminance* :math:`Y_n` as per *CIE 1976* recommendation. Parameters ---------- Y *Luminance* :math:`Y`. Y_n White reference *luminance* :math:`Y_n`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* :math:`L^*`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_star`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`CIETC1-482004m`, :cite:`Wyszecki2000bd` Examples -------- >>> lightness_CIE1976(12.19722535) # doctest: +ELLIPSIS 41.5278758... """ Y = to_domain_100(Y) Y_n = as_float_array(Y_n) L_star = 116 * intermediate_lightness_function_CIE1976(Y, Y_n) - 16 return as_float(from_range_100(L_star))
def yellowness_ASTME313(XYZ): """ Returns the *yellowness* index :math:`YI` of given sample *CIE XYZ* tristimulus values using *ASTM E313* method. ASTM E313 has successfully been used for a variety of white or near white materials. This includes coatings, Plastics, Textiles. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of sample. Returns ------- numeric or ndarray *Whiteness* :math:`YI`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``YI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`X-Rite2012a` Examples -------- >>> import numpy as np >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> yellowness_ASTME313(XYZ) # doctest: +ELLIPSIS 11.0650000... """ _X, Y, Z = tsplit(to_domain_100(XYZ)) WI = 100 * (1 - (0.847 * Z) / Y) return from_range_100(WI)
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 lightness_CIE1976(Y, Y_n=100): """ Returns the *Lightness* :math:`L^*` of 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 *Lightness* :math:`L^*`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L_star`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`CIETC1-482004m`, :cite:`Wyszecki2000bd` Examples -------- >>> lightness_CIE1976(12.19722535) # doctest: +ELLIPSIS 41.5278758... """ Y = to_domain_100(Y) Y_n = as_float_array(Y_n) L_star = 116 * intermediate_lightness_function_CIE1976(Y, Y_n) - 16 return from_range_100(L_star)
def whiteness_ASTME313(XYZ): """ Returns the *whiteness* index :math:`WI` of given sample *CIE XYZ* tristimulus values using *ASTM E313* method. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of sample. Returns ------- numeric or ndarray *Whiteness* :math:`WI`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``WI`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`X-Rite2012a` Examples -------- >>> import numpy as np >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> whiteness_ASTME313(XYZ) # doctest: +ELLIPSIS 55.7400000... """ _X, Y, Z = tsplit(to_domain_100(XYZ)) WI = 3.388 * Z - 3 * Y return from_range_100(WI)
def luminance_Newhall1943(V): """ Returns the *luminance* :math:`R_Y` of given *Munsell* value :math:`V` using *Newhall et al. (1943)* method. Parameters ---------- V : numeric or array_like *Munsell* value :math:`V`. Returns ------- numeric or array_like *luminance* :math:`R_Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``V`` | [0, 10] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``R_Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Newhall1943a` Examples -------- >>> luminance_Newhall1943(4.08244375) # doctest: +ELLIPSIS 12.5500788... """ V = to_domain_10(V) R_Y = (1.2219 * V - 0.23111 * (V * V) + 0.23951 * (V**3) - 0.021009 * (V**4) + 0.0008404 * (V**5)) return from_range_100(R_Y)
def luminance_ASTMD153508(V): """ Returns the *luminance* :math:`Y` of given *Munsell* value :math:`V` using *ASTM D1535-08e1* method. Parameters ---------- V : numeric or array_like *Munsell* value :math:`V`. Returns ------- numeric or array_like *luminance* :math:`Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``V`` | [0, 10] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`ASTMInternational2008a` Examples -------- >>> luminance_ASTMD153508(4.08244375) # doctest: +ELLIPSIS 12.2363426... """ V = to_domain_10(V) Y = (1.1914 * V - 0.22533 * (V ** 2) + 0.23352 * (V ** 3) - 0.020484 * (V ** 4) + 0.00081939 * (V ** 5)) return from_range_100(Y)
def luminance_Newhall1943(V): """ Returns the *luminance* :math:`R_Y` of given *Munsell* value :math:`V` using *Newhall et al. (1943)* method. Parameters ---------- V : numeric or array_like *Munsell* value :math:`V`. Returns ------- numeric or array_like *luminance* :math:`R_Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``V`` | [0, 10] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``R_Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Newhall1943a` Examples -------- >>> luminance_Newhall1943(4.08244375) # doctest: +ELLIPSIS 12.5500788... """ V = to_domain_10(V) R_Y = (1.2219 * V - 0.23111 * (V * V) + 0.23951 * (V ** 3) - 0.021009 * (V ** 4) + 0.0008404 * (V ** 5)) return from_range_100(R_Y)
def luminance_ASTMD153508(V): """ Returns the *luminance* :math:`Y` of given *Munsell* value :math:`V` using *ASTM D1535-08e1* method. Parameters ---------- V : numeric or array_like *Munsell* value :math:`V`. Returns ------- numeric or array_like *luminance* :math:`Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``V`` | [0, 10] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`ASTMInternational2008a` Examples -------- >>> luminance_ASTMD153508(4.08244375) # doctest: +ELLIPSIS 12.2363426... """ V = to_domain_10(V) Y = (1.1914 * V - 0.22533 * (V**2) + 0.23352 * (V**3) - 0.020484 * (V**4) + 0.00081939 * (V**5)) return from_range_100(Y)
def lightness_Glasser1958(Y): """ Returns the *Lightness* :math:`L` of given *luminance* :math:`Y` using *Glasser et al. (1958)* method. Parameters ---------- Y : numeric or array_like *luminance* :math:`Y`. Returns ------- numeric or array_like *Lightness* :math:`L`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Glasser1958a` Examples -------- >>> lightness_Glasser1958(12.19722535) # doctest: +ELLIPSIS 39.8351264... """ Y = to_domain_100(Y) L = 25.29 * spow(Y, 1 / 3) - 18.38 return from_range_100(L)
def luminance_ASTMD1535(V: FloatingOrArrayLike) -> FloatingOrNDArray: """ Return the *luminance* :math:`Y` of given *Munsell* value :math:`V` using *ASTM D1535-08e1* method. Parameters ---------- V *Munsell* value :math:`V`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Luminance* :math:`Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``V`` | [0, 10] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`ASTMInternational2008a` Examples -------- >>> luminance_ASTMD1535(4.08244375) # doctest: +ELLIPSIS 12.2363426... """ V = to_domain_10(V) Y = (1.1914 * V - 0.22533 * (V**2) + 0.23352 * (V**3) - 0.020484 * (V**4) + 0.00081939 * (V**5)) return as_float(from_range_100(Y))
def luminance_Newhall1943(V: FloatingOrArrayLike) -> FloatingOrNDArray: """ Return the *luminance* :math:`R_Y` of given *Munsell* value :math:`V` using *Newhall et al. (1943)* method. Parameters ---------- V *Munsell* value :math:`V`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Luminance* :math:`R_Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``V`` | [0, 10] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``R_Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Newhall1943a` Examples -------- >>> luminance_Newhall1943(4.08244375) # doctest: +ELLIPSIS 12.5500788... """ V = to_domain_10(V) R_Y = (1.2219 * V - 0.23111 * (V * V) + 0.23951 * (V**3) - 0.021009 * (V**4) + 0.0008404 * (V**5)) return as_float(from_range_100(R_Y))
def lightness_Glasser1958(Y: FloatingOrArrayLike) -> FloatingOrNDArray: """ Return the *Lightness* :math:`L` of given *luminance* :math:`Y` using *Glasser et al. (1958)* method. Parameters ---------- Y *Luminance* :math:`Y`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Lightness* :math:`L`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``L`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Glasser1958a` Examples -------- >>> lightness_Glasser1958(12.19722535) # doctest: +ELLIPSIS 39.8351264... """ Y = to_domain_100(Y) L = 25.29 * spow(Y, 1 / 3) - 18.38 return as_float(from_range_100(L))
def CAM16_to_XYZ(CAM16_specification, XYZ_w, L_A, Y_b, surround=CAM16_VIEWING_CONDITIONS['Average'], discount_illuminant=False): """ Converts *CAM16* specification to *CIE XYZ* tristimulus values. This is the *reverse* implementation. Parameters ---------- CAM16_specification : CAM16_Specification *CAM16* colour appearance model specification. Correlate of *Lightness* :math:`J`, correlate of *chroma* :math:`C` or correlate of *colourfulness* :math:`M` and *hue* angle :math:`h` in degrees must be specified, e.g. :math:`JCh` or :math:`JMh`. XYZ_w : array_like *CIE XYZ* tristimulus values of reference white. L_A : numeric or array_like Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`, (often taken to be 20% of the luminance of a white object in the scene). Y_b : numeric or array_like Relative luminance of background :math:`Y_b` in :math:`cd/m^2`. surround : CAM16_InductionFactors, optional Surround viewing conditions. discount_illuminant : bool, optional Discount the illuminant. Returns ------- XYZ : ndarray *CIE XYZ* tristimulus values. Raises ------ ValueError If neither *C* or *M* correlates have been defined in the ``CAM16_specification`` argument. Notes ----- +---------------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +===========================+=======================+===============+ | ``CAM16_specification.h`` | [0, 360] | [0, 1] | +---------------------------+-----------------------+---------------+ | ``CAM16_specification.H`` | [0, 360] | [0, 1] | +---------------------------+-----------------------+---------------+ | ``XYZ_w`` | [0, 100] | [0, 1] | +---------------------------+-----------------------+---------------+ +---------------------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +===========================+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +---------------------------+-----------------------+---------------+ - ``CAM16_specification`` can also be passed as a compatible argument to :func:`colour.utilities.as_namedtuple` definition. References ---------- :cite:`Li2017` Examples -------- >>> specification = CAM16_Specification(J=41.731207905126638, ... C=0.103355738709070, ... h=217.067959767393010) >>> XYZ_w = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> Y_b = 20.0 >>> CAM16_to_XYZ(specification, XYZ_w, L_A, Y_b) # doctest: +ELLIPSIS array([ 19.01..., 20... , 21.78...]) """ J, C, h, _s, _Q, M, _H, _HC = as_namedtuple(CAM16_specification, CAM16_Specification) L_A = as_float_array(L_A) h = to_domain_degrees(h) XYZ_w = to_domain_100(XYZ_w) _X_w, Y_w, _Z_w = tsplit(XYZ_w) # Step 0 # Converting *CIE XYZ* tristimulus values to sharpened *RGB* values. RGB_w = dot_vector(M_16, XYZ_w) # Computing degree of adaptation :math:`D`. D = (np.clip(degree_of_adaptation(surround.F, L_A), 0, 1) if not discount_illuminant else np.ones(L_A.shape)) n, F_L, N_bb, N_cb, z = tsplit( viewing_condition_dependent_parameters(Y_b, Y_w, L_A)) D_RGB = (D[..., np.newaxis] * Y_w[..., np.newaxis] / RGB_w + 1 - D[..., np.newaxis]) RGB_wc = D_RGB * RGB_w # Applying forward post-adaptation non linear response compression. RGB_aw = post_adaptation_non_linear_response_compression_forward( RGB_wc, F_L) # Computing achromatic responses for the whitepoint. A_w = achromatic_response_forward(RGB_aw, N_bb) # Step 1 if C is None and M is not None: C = M / spow(F_L, 0.25) elif C is None: raise ValueError('Either "C" or "M" correlate must be defined in ' 'the "CAM16_specification" argument!') # Step 2 # Computing temporary magnitude quantity :math:`t`. t = temporary_magnitude_quantity_reverse(C, J, n) # Computing eccentricity factor *e_t*. e_t = eccentricity_factor(h) # Computing achromatic response :math:`A` for the stimulus. A = achromatic_response_reverse(A_w, J, surround.c, z) # Computing *P_1* to *P_3*. P_n = P(surround.N_c, N_cb, e_t, t, A, N_bb) _P_1, P_2, _P_3 = tsplit(P_n) # Step 3 # Computing opponent colour dimensions :math:`a` and :math:`b`. a, b = tsplit(opponent_colour_dimensions_reverse(P_n, h)) # Step 4 # Computing post-adaptation non linear response compression matrix. RGB_a = post_adaptation_non_linear_response_compression_matrix(P_2, a, b) # Step 5 # Applying reverse post-adaptation non linear response compression. RGB_c = post_adaptation_non_linear_response_compression_reverse(RGB_a, F_L) # Step 6 RGB = RGB_c / D_RGB # Step 7 XYZ = dot_vector(M_16_INVERSE, RGB) return from_range_100(XYZ)
def UVW_to_XYZ( UVW, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts *CIE 1964 U\\*V\\*W\\** colourspace to *CIE XYZ* tristimulus values. Parameters ---------- UVW : array_like *CIE 1964 U\\*V\\*W\\** colourspace array. illuminant : array_like, optional Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE XYZ* tristimulus values. Warning ------- The input domain and output range of that definition are non standard! Notes ----- +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``UVW`` | ``U`` : [-100, 100] | ``U`` : [-1, 1] | | | | | | | ``V`` : [-100, 100] | ``V`` : [-1, 1] | | | | | | | ``W`` : [0, 100] | ``W`` : [0, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`Wikipedia2008a` Examples -------- >>> import numpy as np >>> UVW = np.array([94.55035725, 11.55536523, 40.54757405]) >>> UVW_to_XYZ(UVW) array([ 20.654008, 12.197225, 5.136952]) """ U, V, W = tsplit(to_domain_100(UVW)) u_0, v_0 = tsplit(xy_to_UCS_uv(xyY_to_xy(illuminant))) Y = ((W + 17) / 25) ** 3 u = U / (13 * W) + u_0 v = V / (13 * W) + v_0 x, y = tsplit(UCS_uv_to_xy(tstack([u, v]))) XYZ = xyY_to_XYZ(tstack([x, y, Y])) return from_range_100(XYZ)
def XYZ_to_Luv( XYZ, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts from *CIE XYZ* tristimulus values to *CIE L\\*u\\*v\\** colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. illuminant : array_like, optional Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE L\\*u\\*v\\** colourspace array. Notes ----- +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``Luv`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``u`` : [-100, 100] | ``u`` : [-1, 1] | | | | | | | ``v`` : [-100, 100] | ``v`` : [-1, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004m`, :cite:`Wikipedia2007b` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_Luv(XYZ) # doctest: +ELLIPSIS array([ 41.5278752..., 96.8362605..., 17.7521014...]) """ X, Y, Z = tsplit(to_domain_1(XYZ)) X_r, Y_r, Z_r = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) with domain_range_scale('100'): L = lightness_CIE1976(Y, Y_r) u = (13 * L * ((4 * X / (X + 15 * Y + 3 * Z)) - (4 * X_r / (X_r + 15 * Y_r + 3 * Z_r)))) v = (13 * L * ((9 * Y / (X + 15 * Y + 3 * Z)) - (9 * Y_r / (X_r + 15 * Y_r + 3 * Z_r)))) Luv = tstack([L, u, v]) return from_range_100(Luv)
def XYZ_to_Lab( XYZ, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts from *CIE XYZ* tristimulus values to *CIE L\\*a\\*b\\** colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. illuminant : array_like, optional Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE L\\*a\\*b\\** colourspace array. Notes ----- +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``Lab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``a`` : [-100, 100] | ``a`` : [-1, 1] | | | | | | | ``b`` : [-100, 100] | ``b`` : [-1, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004m` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_Lab(XYZ) # doctest: +ELLIPSIS array([ 41.5278752..., 52.6385830..., 26.9231792...]) """ X, Y, Z = tsplit(to_domain_1(XYZ)) X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) f_X_X_n = intermediate_lightness_function_CIE1976(X, X_n) f_Y_Y_n = intermediate_lightness_function_CIE1976(Y, Y_n) f_Z_Z_n = intermediate_lightness_function_CIE1976(Z, Z_n) L = 116 * f_Y_Y_n - 16 a = 500 * (f_X_X_n - f_Y_Y_n) b = 200 * (f_Y_Y_n - f_Z_Z_n) Lab = tstack([L, a, b]) return from_range_100(Lab)
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 CIECAM02_to_XYZ(CIECAM02_specification, XYZ_w, L_A, Y_b, surround=CIECAM02_VIEWING_CONDITIONS['Average'], discount_illuminant=False): """ Converts *CIECAM02* specification to *CIE XYZ* tristimulus values. This is the *reverse* implementation. Parameters ---------- CIECAM02_specification : CIECAM02_Specification *CIECAM02* colour appearance model specification. Correlate of *Lightness* :math:`J`, correlate of *chroma* :math:`C` or correlate of *colourfulness* :math:`M` and *hue* angle :math:`h` in degrees must be specified, e.g. :math:`JCh` or :math:`JMh`. XYZ_w : array_like *CIE XYZ* tristimulus values of reference white. L_A : numeric or array_like Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`, (often taken to be 20% of the luminance of a white object in the scene). Y_b : numeric or array_like Relative luminance of background :math:`Y_b` in :math:`cd/m^2`. surround : CIECAM02_InductionFactors, optional Surround viewing conditions. discount_illuminant : bool, optional Discount the illuminant. Returns ------- XYZ : ndarray *CIE XYZ* tristimulus values. Raises ------ ValueError If neither *C* or *M* correlates have been defined in the ``CIECAM02_specification`` argument. Warning ------- The output range of that definition is non standard! Notes ----- +------------------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +==============================+=======================+===============+ | ``CIECAM02_specification.h`` | [0, 360] | [0, 1] | +------------------------------+-----------------------+---------------+ | ``CIECAM02_specification.H`` | [0, 360] | [0, 1] | +------------------------------+-----------------------+---------------+ | ``XYZ_w`` | [0, 100] | [0, 1] | +------------------------------+-----------------------+---------------+ +------------------------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +==============================+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------------------------+-----------------------+---------------+ - ``CIECAM02_specification`` can also be passed as a compatible argument to :func:`colour.utilities.as_namedtuple` definition. References ---------- :cite:`Fairchild2004c`, :cite:`Luo2013`, :cite:`Moroneya`, :cite:`Wikipedia2007a` Examples -------- >>> specification = CIECAM02_Specification(J=41.731091132513917, ... C=0.104707757171031, ... h=219.048432658311780) >>> XYZ_w = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> Y_b = 20.0 >>> CIECAM02_to_XYZ(specification, XYZ_w, L_A, Y_b) # doctest: +ELLIPSIS array([ 19.01..., 20... , 21.78...]) """ J, C, h, _s, _Q, M, _H, _HC = as_namedtuple(CIECAM02_specification, CIECAM02_Specification) L_A = as_float_array(L_A) h = to_domain_degrees(h) XYZ_w = to_domain_100(XYZ_w) _X_w, Y_w, _Z_w = tsplit(XYZ_w) n, F_L, N_bb, N_cb, z = tsplit( viewing_condition_dependent_parameters(Y_b, Y_w, L_A)) if C is None and M is not None: C = M / spow(F_L, 0.25) elif C is None: raise ValueError('Either "C" or "M" correlate must be defined in ' 'the "CIECAM02_specification" argument!') # Converting *CIE XYZ* tristimulus values to *CMCCAT2000* transform # sharpened *RGB* values. RGB_w = dot_vector(CAT02_CAT, XYZ_w) # Computing degree of adaptation :math:`D`. D = (degree_of_adaptation(surround.F, L_A) if not discount_illuminant else np.ones(L_A.shape)) # Computing full chromatic adaptation. RGB_wc = full_chromatic_adaptation_forward(RGB_w, RGB_w, Y_w, D) # Converting to *Hunt-Pointer-Estevez* colourspace. RGB_pw = RGB_to_rgb(RGB_wc) # Applying post-adaptation non linear response compression. RGB_aw = post_adaptation_non_linear_response_compression_forward( RGB_pw, F_L) # Computing achromatic response for the whitepoint. A_w = achromatic_response_forward(RGB_aw, N_bb) # Computing temporary magnitude quantity :math:`t`. t = temporary_magnitude_quantity_reverse(C, J, n) # Computing eccentricity factor *e_t*. e_t = eccentricity_factor(h) # Computing achromatic response :math:`A` for the stimulus. A = achromatic_response_reverse(A_w, J, surround.c, z) # Computing *P_1* to *P_3*. P_n = P(surround.N_c, N_cb, e_t, t, A, N_bb) _P_1, P_2, _P_3 = tsplit(P_n) # Computing opponent colour dimensions :math:`a` and :math:`b`. a, b = tsplit(opponent_colour_dimensions_reverse(P_n, h)) # Computing post-adaptation non linear response compression matrix. RGB_a = post_adaptation_non_linear_response_compression_matrix(P_2, a, b) # Applying reverse post-adaptation non linear response compression. RGB_p = post_adaptation_non_linear_response_compression_reverse(RGB_a, F_L) # Converting to *Hunt-Pointer-Estevez* colourspace. RGB_c = rgb_to_RGB(RGB_p) # Applying reverse full chromatic adaptation. RGB = full_chromatic_adaptation_reverse(RGB_c, RGB_w, Y_w, D) # Converting *CMCCAT2000* transform sharpened *RGB* values to *CIE XYZ* # tristimulus values. XYZ = dot_vector(CAT02_INVERSE_CAT, RGB) return from_range_100(XYZ)
def JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients): """ Converts from *CIECAM02* :math:`JMh` correlates array to one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. The :math:`JMh` correlates array is constructed using the CIECAM02 correlate of *Lightness* :math:`J`, the *CIECAM02* correlate of *colourfulness* :math:`M` and the *CIECAM02* *Hue* angle :math:`h` in degrees. Parameters ---------- JMh : array_like *CIECAM02* correlates array :math:`JMh`. coefficients : array_like Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces. Returns ------- ndarray *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | | | | | | | ``M`` : [0, 100] | ``M`` : [0, 1] | | | | | | | ``h`` : [0, 360] | ``h`` : [0, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | | | | | | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | | | | | | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | +------------+------------------------+--------------------+ Examples -------- >>> from colour.appearance import ( ... CIECAM02_VIEWING_CONDITIONS, ... XYZ_to_CIECAM02) >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_w = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> Y_b = 20.0 >>> surround = CIECAM02_VIEWING_CONDITIONS['Average'] >>> specification = XYZ_to_CIECAM02( ... XYZ, XYZ_w, L_A, Y_b, surround) >>> JMh = (specification.J, specification.M, specification.h) >>> JMh_CIECAM02_to_UCS_Luo2006(JMh, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) ... # doctest: +ELLIPSIS array([ 54.9043313..., -0.0845039..., -0.0685483...]) """ J, M, h = tsplit(JMh) J = to_domain_100(J) M = to_domain_100(M) h = to_domain_degrees(h) _K_L, c_1, c_2 = tsplit(coefficients) J_p = ((1 + 100 * c_1) * J) / (1 + c_1 * J) M_p = (1 / c_2) * np.log(1 + c_2 * M) a_p, b_p = tsplit(polar_to_cartesian(tstack([M_p, np.radians(h)]))) Jpapbp = tstack([J_p, a_p, b_p]) return from_range_100(Jpapbp)
def UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, coefficients): """ Converts from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array to *CIECAM02* :math:`JMh` correlates array. Parameters ---------- Jpapbp : array_like *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. coefficients : array_like Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces. Returns ------- ndarray *CIECAM02* correlates array :math:`JMh`. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | | | | | | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | | | | | | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | | | | | | | ``M`` : [0, 100] | ``M`` : [0, 1] | | | | | | | ``h`` : [0, 360] | ``h`` : [0, 1] | +------------+------------------------+--------------------+ Examples -------- >>> Jpapbp = np.array([54.90433134, -0.08450395, -0.06854831]) >>> UCS_Luo2006_to_JMh_CIECAM02( ... Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) ... # doctest: +ELLIPSIS array([ 4.1731091...e+01, 1.0884217...e-01, 2.1904843...e+02]) """ J_p, a_p, b_p = tsplit(to_domain_100(Jpapbp)) _K_L, c_1, c_2 = tsplit(coefficients) J = -J_p / (c_1 * J_p - 1 - 100 * c_1) M_p, h = tsplit(cartesian_to_polar(tstack([a_p, b_p]))) M = (np.exp(M_p / (1 / c_2)) - 1) / c_2 JMh = tstack([ from_range_100(J), from_range_100(M), from_range_degrees(np.degrees(h) % 360) ]) return JMh
def Lab_to_DIN99(Lab, k_E=1, k_CH=1): """ Converts from *CIE L\\*a\\*b\\** colourspace to *DIN99* colourspace. Parameters ---------- Lab : array_like *CIE L\\*a\\*b\\** colourspace array. k_E : numeric, optional Parametric factor :math:`K_E` used to compensate for texture and other specimen presentation effects. k_CH : numeric, optional Parametric factor :math:`K_{CH}` used to compensate for texture and other specimen presentation effects. Returns ------- ndarray *DIN99* colourspace array. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Lab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``a`` : [-100, 100] | ``a`` : [-1, 1] | | | | | | | ``b`` : [-100, 100] | ``b`` : [-1, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Lab_99`` | ``L_99`` : [0, 100] | ``L_99`` : [0, 1] | | | | | | | ``a_99`` : [-100, 100] | ``a_99`` : [-1, 1] | | | | | | | ``b_99`` : [-100, 100] | ``b_99`` : [-1, 1] | +------------+------------------------+--------------------+ References ---------- :cite:`ASTMInternational2007` Examples -------- >>> import numpy as np >>> Lab = np.array([41.52787529, 52.63858304, 26.92317922]) >>> Lab_to_DIN99(Lab) # doctest: +ELLIPSIS array([ 53.2282198..., 28.4163465..., 3.8983955...]) """ L, a, b = tsplit(to_domain_100(Lab)) cos_16 = np.cos(np.radians(16)) sin_16 = np.sin(np.radians(16)) e = cos_16 * a + sin_16 * b f = 0.7 * (-sin_16 * a + cos_16 * b) G = spow(e ** 2 + f ** 2, 0.5) h_ef = np.arctan2(f, e) C_99 = (np.log(1 + 0.045 * G)) / (0.045 * k_CH * k_E) # Hue angle is unused currently. # h_99 = np.degrees(h_ef) a_99 = C_99 * np.cos(h_ef) b_99 = C_99 * np.sin(h_ef) L_99 = 105.509 * (np.log(1 + 0.0158 * L)) * k_E Lab_99 = tstack([L_99, a_99, b_99]) return from_range_100(Lab_99)
def DIN99_to_Lab(Lab_99, k_E=1, k_CH=1): """ Converts from *DIN99* colourspace to *CIE L\\*a\\*b\\** colourspace. Parameters ---------- Lab_99 : array_like *DIN99* colourspace array. k_E : numeric, optional Parametric factor :math:`K_E` used to compensate for texture and other specimen presentation effects. k_CH : numeric, optional Parametric factor :math:`K_{CH}` used to compensate for texture and other specimen presentation effects. Returns ------- ndarray *CIE L\\*a\\*b\\** colourspace array. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Lab_99`` | ``L_99`` : [0, 100] | ``L_99`` : [0, 1] | | | | | | | ``a_99`` : [-100, 100] | ``a_99`` : [-1, 1] | | | | | | | ``b_99`` : [-100, 100] | ``b_99`` : [-1, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Lab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``a`` : [-100, 100] | ``a`` : [-1, 1] | | | | | | | ``b`` : [-100, 100] | ``b`` : [-1, 1] | +------------+------------------------+--------------------+ References ---------- :cite:`ASTMInternational2007` Examples -------- >>> import numpy as np >>> Lab_99 = np.array([53.22821988, 28.41634656, 3.89839552]) >>> DIN99_to_Lab(Lab_99) # doctest: +ELLIPSIS array([ 41.5278752..., 52.6385830..., 26.9231792...]) """ L_99, a_99, b_99 = tsplit(to_domain_100(Lab_99)) cos_16 = np.cos(np.radians(16)) sin_16 = np.sin(np.radians(16)) h_99 = np.arctan2(b_99, a_99) C_99 = np.sqrt(a_99 ** 2 + b_99 ** 2) G = (np.exp(0.045 * C_99 * k_CH * k_E) - 1) / 0.045 e = G * np.cos(h_99) f = G * np.sin(h_99) a = e * cos_16 - (f / 0.7) * sin_16 b = e * sin_16 + (f / 0.7) * cos_16 L = (np.exp(L_99 * k_E / 105.509) - 1) / 0.0158 Lab = tstack([L, a, b]) return from_range_100(Lab)
def XYZ_to_hdr_IPT(XYZ, Y_s=0.2, Y_abs=100, method='Fairchild 2011'): """ Converts from *CIE XYZ* tristimulus values to *hdr-IPT* colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Y_s : numeric or array_like Relative luminance :math:`Y_s` of the surround. Y_abs : numeric or array_like Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. method : unicode, optional **{'Fairchild 2011', 'Fairchild 2010'}**, Computation method. Returns ------- ndarray *hdr-IPT* colourspace array. Notes ----- +-------------+-------------------------+---------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=========================+=====================+ | ``XYZ`` | [0, 1] | [0, 1] | +-------------+-------------------------+---------------------+ | ``Y_s`` | [0, 1] | [0, 1] | +-------------+-------------------------+---------------------+ +-------------+-------------------------+---------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +=============+=========================+=====================+ | ``IPT_hdr`` | ``I_hdr`` : [0, 100] | ``I_hdr`` : [0, 1] | | | | | | | ``P_hdr`` : [-100, 100] | ``P_hdr`` : [-1, 1] | | | | | | | ``T_hdr`` : [-100, 100] | ``T_hdr`` : [-1, 1] | +-------------+-------------------------+---------------------+ - Input *CIE XYZ* tristimulus values needs to be adapted for *CIE Standard Illuminant D Series* *D65*. References ---------- :cite:`Fairchild2010`, :cite:`Fairchild2011` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_hdr_IPT(XYZ) # doctest: +ELLIPSIS array([ 48.3937634..., 42.4499020..., 22.0195403...]) >>> XYZ_to_hdr_IPT(XYZ, method='Fairchild 2010') # doctest: +ELLIPSIS array([ 30.0287314..., 83.9384506..., 34.9028738...]) """ XYZ = to_domain_1(XYZ) method_l = method.lower() assert method.lower() in [ m.lower() for m in HDR_IPT_METHODS ], ('"{0}" method is invalid, must be one of {1}!'.format( method, HDR_IPT_METHODS)) if method_l == 'fairchild 2010': lightness_callable = lightness_Fairchild2010 else: lightness_callable = lightness_Fairchild2011 e = exponent_hdr_IPT(Y_s, Y_abs, method)[..., np.newaxis] LMS = dot_vector(IPT_XYZ_TO_LMS_MATRIX, XYZ) # Domain and range scaling has already be handled. with domain_range_scale('ignore'): LMS_prime = np.sign(LMS) * np.abs(lightness_callable(LMS, e)) IPT_hdr = dot_vector(IPT_LMS_TO_IPT_MATRIX, LMS_prime) return from_range_100(IPT_hdr)
def XYZ_to_spectral( XYZ, cmfs=colour.STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'], interval=5, tolerance=1e-10, maximum_iterations=5000, illuminant=sd_ones(), max_refl=1.0): XYZ = to_domain_1(XYZ) shape = SpectralShape(cmfs.shape.start, cmfs.shape.end, interval) cmfs = cmfs.copy().align(shape) illuminant = illuminant.copy().align(shape) spd = sd_zeros(shape) def function_objective(a): """ Objective function. """ return np.sum(np.diff(a)**2) def function_constraint(a): """ Function defining the constraint for XYZ=XYZ. """ spd[:] = np.exp(a) return (XYZ - (colour.colorimetry.spectral_to_XYZ_integration( spd, cmfs=cmfs, illuminant=illuminant))) def function_constraint2(a): """ Function defining constraint on emission/reflectance """ if max_refl <= 0.0: return 0.0 return max_refl - np.exp(np.max(a)) * 100. wavelengths = spd.wavelengths bins = wavelengths.size constraints = ({'type': 'eq', 'fun': function_constraint}, {'type': 'ineq', 'fun': function_constraint2}) result = minimize( function_objective, spd.values, method='SLSQP', constraints=constraints, options={ 'ftol': tolerance, 'maxiter': maximum_iterations, 'disp': True }) if not result.success: raise RuntimeError( 'Optimization failed for {0} after {1} iterations: "{2}".'.format( XYZ, result.nit, result.message)) return SpectralDistribution( from_range_100(np.exp(result.x) * 100), wavelengths, name='Meng (2015) - {0}'.format(XYZ))
def chromatic_adaptation_forward_CMCCAT2000( XYZ, XYZ_w, XYZ_wr, L_A1, L_A2, surround=CMCCAT2000_VIEWING_CONDITIONS['Average']): """ Adapts given stimulus *CIE XYZ* tristimulus values from test viewing conditions to reference viewing conditions using *CMCCAT2000* forward chromatic adaptation model. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of the stimulus to adapt. XYZ_w : array_like Test viewing condition *CIE XYZ* tristimulus values of the whitepoint. XYZ_wr : array_like Reference viewing condition *CIE XYZ* tristimulus values of the whitepoint. L_A1 : numeric or array_like Luminance of test adapting field :math:`L_{A1}` in :math:`cd/m^2`. L_A2 : numeric or array_like Luminance of reference adapting field :math:`L_{A2}` in :math:`cd/m^2`. surround : CMCCAT2000_InductionFactors, optional Surround viewing conditions induction factors. Returns ------- ndarray *CIE XYZ_c* tristimulus values of the stimulus corresponding colour. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_w`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wr`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_c`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Li2002a`, :cite:`Westland2012k` Examples -------- >>> XYZ = np.array([22.48, 22.74, 8.54]) >>> XYZ_w = np.array([111.15, 100.00, 35.20]) >>> XYZ_wr = np.array([94.81, 100.00, 107.30]) >>> L_A1 = 200 >>> L_A2 = 200 >>> chromatic_adaptation_forward_CMCCAT2000(XYZ, XYZ_w, XYZ_wr, L_A1, L_A2) ... # doctest: +ELLIPSIS array([ 19.5269832..., 23.0683396..., 24.9717522...]) """ XYZ = to_domain_100(XYZ) XYZ_w = to_domain_100(XYZ_w) XYZ_wr = to_domain_100(XYZ_wr) L_A1 = as_float_array(L_A1) L_A2 = as_float_array(L_A2) RGB = dot_vector(CMCCAT2000_CAT, XYZ) RGB_w = dot_vector(CMCCAT2000_CAT, XYZ_w) RGB_wr = dot_vector(CMCCAT2000_CAT, XYZ_wr) D = (surround.F * (0.08 * np.log10(0.5 * (L_A1 + L_A2)) + 0.76 - 0.45 * (L_A1 - L_A2) / (L_A1 + L_A2))) D = np.clip(D, 0, 1) a = D * XYZ_w[..., 1] / XYZ_wr[..., 1] RGB_c = ( RGB * (a[..., np.newaxis] * (RGB_wr / RGB_w) + 1 - D[..., np.newaxis])) XYZ_c = dot_vector(CMCCAT2000_INVERSE_CAT, RGB_c) return from_range_100(XYZ_c)
def OSA_UCS_to_XYZ(Ljg, optimisation_parameters=None): """ Converts from *OSA UCS* colourspace to *CIE XYZ* tristimulus values under the *CIE 1964 10 Degree Standard Observer*. Parameters ---------- Ljg : array_like *OSA UCS* :math:`Ljg` lightness, jaune (yellowness), and greenness. optimisation_parameters : dict_like, optional Parameters for :func:`scipy.optimize.fmin` definition. Returns ------- ndarray *CIE XYZ* tristimulus values under the *CIE 1964 10 Degree Standard Observer*. Warnings -------- There is no analytical reverse transformation from *OSA UCS* to :math:`Ljg` lightness, jaune (yellowness), and greenness to *CIE XYZ* tristimulus values, the current implementation relies on optimization using :func:`scipy.optimize.fmin` definition and thus has reduced precision and poor performance. Notes ----- +------------+-----------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+====================+ | ``Ljg`` | ``L`` : [-100, 100] | ``L`` : [-1, 1] | | | | | | | ``j`` : [-100, 100] | ``j`` : [-1, 1] | | | | | | | ``g`` : [-100, 100] | ``g`` : [-1, 1] | +------------+-----------------------+--------------------+ +------------+-----------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+====================+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+--------------------+ - *OSA UCS* uses the *CIE 1964 10 Degree Standard Observer*. References ---------- :cite:`Cao2013`, :cite:`Moroney2003` Examples -------- >>> import numpy as np >>> Ljg = np.array([-3.00499790, 2.99713697, -9.66784231]) >>> OSA_UCS_to_XYZ(Ljg) # doctest: +ELLIPSIS array([ 20.6540240..., 12.1972369..., 5.1369372...]) """ Ljg = to_domain_100(Ljg) shape = Ljg.shape Ljg = np.atleast_1d(Ljg.reshape([-1, 3])) optimisation_settings = {'disp': False} if optimisation_parameters is not None: optimisation_settings.update(optimisation_parameters) def error_function(XYZ, Ljg): """ Error function. """ # Error must be computed in "reference" domain and range. with domain_range_scale('ignore'): error = np.linalg.norm(XYZ_to_OSA_UCS(XYZ) - Ljg) return error x_0 = np.array([30, 30, 30]) XYZ = np.array([ fmin(error_function, x_0, (Ljg_i, ), **optimisation_settings) for Ljg_i in Ljg ]) return from_range_100(XYZ.reshape(shape))
def XYZ_to_OSA_UCS(XYZ): """ Converts from *CIE XYZ* tristimulus values under the *CIE 1964 10 Degree Standard Observer* to *OSA UCS* colourspace. The lightness axis, *L* is usually in range [-9, 5] and centered around middle gray (Munsell N/6). The yellow-blue axis, *j* is usually in range [-15, 15]. The red-green axis, *g* is usually in range [-20, 15]. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values under the *CIE 1964 10 Degree Standard Observer*. Returns ------- ndarray *OSA UCS* :math:`Ljg` lightness, jaune (yellowness), and greenness. Notes ----- +------------+-----------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+====================+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+--------------------+ +------------+-----------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+====================+ | ``Ljg`` | ``L`` : [-100, 100] | ``L`` : [-1, 1] | | | | | | | ``j`` : [-100, 100] | ``j`` : [-1, 1] | | | | | | | ``g`` : [-100, 100] | ``g`` : [-1, 1] | +------------+-----------------------+--------------------+ - *OSA UCS* uses the *CIE 1964 10 Degree Standard Observer*. References ---------- :cite:`Cao2013`, :cite:`Moroney2003` Examples -------- >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 >>> XYZ_to_OSA_UCS(XYZ) # doctest: +ELLIPSIS array([-3.0049979..., 2.9971369..., -9.6678423...]) """ XYZ = to_domain_100(XYZ) x, y, Y = tsplit(XYZ_to_xyY(XYZ)) Y_0 = Y * (4.4934 * x ** 2 + 4.3034 * y ** 2 - 4.276 * x * y - 1.3744 * x - 2.5643 * y + 1.8103) o_3 = 1 / 3 Y_0_es = spow(Y_0, o_3) - 2 / 3 # Gracefully handles Y_0 < 30. Y_0_s = Y_0 - 30 Lambda = 5.9 * (Y_0_es + 0.042 * spow(Y_0_s, o_3)) RGB = dot_vector(M_XYZ_TO_RGB_OSA_UCS, XYZ) RGB_3 = spow(RGB, 1 / 3) C = Lambda / (5.9 * Y_0_es) L = (Lambda - 14.4) / spow(2, 1 / 2) j = C * np.dot(RGB_3, np.array([1.7, 8, -9.7])) g = C * np.dot(RGB_3, np.array([-13.7, 17.7, -4])) Ljg = tstack([L, j, g]) return from_range_100(Ljg)
def XYZ_to_UVW( XYZ, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts from *CIE XYZ* tristimulus values to *CIE 1964 U\\*V\\*W\\** colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. illuminant : array_like, optional Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE 1964 U\\*V\\*W\\** colourspace array. Warning ------- The input domain and output range of that definition are non standard! Notes ----- +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``UVW`` | ``U`` : [-100, 100] | ``U`` : [-1, 1] | | | | | | | ``V`` : [-100, 100] | ``V`` : [-1, 1] | | | | | | | ``W`` : [0, 100] | ``W`` : [0, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`Wikipedia2008a` Examples -------- >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 >>> XYZ_to_UVW(XYZ) # doctest: +ELLIPSIS array([ 94.5503572..., 11.5553652..., 40.5475740...]) """ XYZ = to_domain_100(XYZ) xy = xyY_to_xy(illuminant) xyY = XYZ_to_xyY(XYZ, xy) _x, _y, Y = tsplit(xyY) u, v = tsplit(UCS_to_uv(XYZ_to_UCS(XYZ))) u_0, v_0 = tsplit(xy_to_UCS_uv(xy)) W = 25 * spow(Y, 1 / 3) - 17 U = 13 * W * (u - u_0) V = 13 * W * (v - v_0) UVW = tstack([U, V, W]) return from_range_100(UVW)
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)