def test_get_domain_range_scale(self): """ Test :func:`colour.utilities.common.get_domain_range_scale` definition. """ with domain_range_scale("Reference"): self.assertEqual(get_domain_range_scale(), "reference") with domain_range_scale("1"): self.assertEqual(get_domain_range_scale(), "1") with domain_range_scale("100"): self.assertEqual(get_domain_range_scale(), "100")
def test_get_domain_range_scale(self): """ Tests :func:`colour.utilities.common.get_domain_range_scale` definition. """ with domain_range_scale('Reference'): self.assertEqual(get_domain_range_scale(), 'reference') with domain_range_scale('1'): self.assertEqual(get_domain_range_scale(), '1') with domain_range_scale('100'): self.assertEqual(get_domain_range_scale(), '100')
def test_get_domain_range_scale(self): """ Tests :func:`colour.utilities.common.get_domain_range_scale` definition. """ with domain_range_scale('Reference'): self.assertEqual(get_domain_range_scale(), 'reference') with domain_range_scale('1'): self.assertEqual(get_domain_range_scale(), '1') with domain_range_scale('100'): self.assertEqual(get_domain_range_scale(), '100')
def delta_E_DIN99(Lab_1, Lab_2, textiles=False): """ Returns the difference :math:`\\Delta E_{DIN99}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *DIN99* formula. Parameters ---------- Lab_1 : array_like *CIE L\\*a\\*b\\** colourspace array 1. Lab_2 : array_like *CIE L\\*a\\*b\\** colourspace array 2. Returns ------- numeric or ndarray Colour difference :math:`\\Delta E_{DIN99}`. Notes ----- +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ | ``Lab_1`` | ``L_1`` : [0, 100] | ``L_1`` : [0, 1] | | | | | | | ``a_1`` : [-100, 100] | ``a_1`` : [-1, 1] | | | | | | | ``b_1`` : [-100, 100] | ``b_1`` : [-1, 1] | +------------+-----------------------+-------------------+ | ``Lab_2`` | ``L_2`` : [0, 100] | ``L_2`` : [0, 1] | | | | | | | ``a_2`` : [-100, 100] | ``a_2`` : [-1, 1] | | | | | | | ``b_2`` : [-100, 100] | ``b_2`` : [-1, 1] | +------------+-----------------------+-------------------+ References ---------- :cite:`ASTMInternational2007` Examples -------- >>> import numpy as np >>> Lab_1 = np.array([60.2574, -34.0099, 36.2677]) >>> Lab_2 = np.array([60.4626, -34.1751, 39.4387]) >>> delta_E_DIN99(Lab_1, Lab_2) # doctest: +ELLIPSIS 1.1772166... """ k_E = 2 if textiles else 1 k_CH = 0.5 if textiles else 1 factor = 100 if get_domain_range_scale() == '1' else 1 d_E = euclidean_distance( Lab_to_DIN99(Lab_1, k_E, k_CH) * factor, Lab_to_DIN99(Lab_2, k_E, k_CH) * factor) return d_E
def delta_E_DIN99(Lab_1, Lab_2, textiles=False): """ Returns the difference :math:`\\Delta E_{DIN99}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *DIN99* formula. Parameters ---------- Lab_1 : array_like *CIE L\\*a\\*b\\** colourspace array 1. Lab_2 : array_like *CIE L\\*a\\*b\\** colourspace array 2. Returns ------- numeric or ndarray Colour difference :math:`\\Delta E_{DIN99}`. Notes ----- +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ | ``Lab_1`` | ``L_1`` : [0, 100] | ``L_1`` : [0, 1] | | | | | | | ``a_1`` : [-100, 100] | ``a_1`` : [-1, 1] | | | | | | | ``b_1`` : [-100, 100] | ``b_1`` : [-1, 1] | +------------+-----------------------+-------------------+ | ``Lab_2`` | ``L_2`` : [0, 100] | ``L_2`` : [0, 1] | | | | | | | ``a_2`` : [-100, 100] | ``a_2`` : [-1, 1] | | | | | | | ``b_2`` : [-100, 100] | ``b_2`` : [-1, 1] | +------------+-----------------------+-------------------+ References ---------- :cite:`ASTMInternational2007` Examples -------- >>> import numpy as np >>> Lab_1 = np.array([60.2574, -34.0099, 36.2677]) >>> Lab_2 = np.array([60.4626, -34.1751, 39.4387]) >>> delta_E_DIN99(Lab_1, Lab_2) # doctest: +ELLIPSIS 1.1772166... """ k_E = 2 if textiles else 1 k_CH = 0.5 if textiles else 1 factor = 100 if get_domain_range_scale() == '1' else 1 d_E = euclidean_distance( Lab_to_DIN99(Lab_1, k_E, k_CH) * factor, Lab_to_DIN99(Lab_2, k_E, k_CH) * factor) return d_E
def test_set_domain_range_scale(self): """ Test :func:`colour.utilities.common.set_domain_range_scale` definition. """ with domain_range_scale("Reference"): set_domain_range_scale("1") self.assertEqual(get_domain_range_scale(), "1") with domain_range_scale("Reference"): set_domain_range_scale("100") self.assertEqual(get_domain_range_scale(), "100") with domain_range_scale("1"): set_domain_range_scale("Reference") self.assertEqual(get_domain_range_scale(), "reference") self.assertRaises(ValueError, lambda: set_domain_range_scale("Invalid"))
def test_set_domain_range_scale(self): """ Tests :func:`colour.utilities.common.set_domain_range_scale` definition. """ with domain_range_scale('Reference'): set_domain_range_scale('1') self.assertEqual(get_domain_range_scale(), '1') with domain_range_scale('Reference'): set_domain_range_scale('100') self.assertEqual(get_domain_range_scale(), '100') with domain_range_scale('1'): set_domain_range_scale('Reference') self.assertEqual(get_domain_range_scale(), 'reference') self.assertRaises(AssertionError, lambda: set_domain_range_scale('Invalid'))
def test_set_domain_range_scale(self): """ Tests :func:`colour.utilities.common.set_domain_range_scale` definition. """ with domain_range_scale('Reference'): set_domain_range_scale('1') self.assertEqual(get_domain_range_scale(), '1') with domain_range_scale('Reference'): set_domain_range_scale('100') self.assertEqual(get_domain_range_scale(), '100') with domain_range_scale('1'): set_domain_range_scale('Reference') self.assertEqual(get_domain_range_scale(), 'reference') self.assertRaises(AssertionError, lambda: set_domain_range_scale('Invalid'))
def whiteness(XYZ, XYZ_0, method='CIE 2004', **kwargs): """ Returns the *whiteness* :math:`W` using given method. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of sample. XYZ_0 : array_like *CIE XYZ* tristimulus values of reference white. method : unicode, optional **{'CIE 2004', 'Berger 1959', 'Taube 1960', 'Stensby 1968', 'ASTM E313', 'Ganz 1979'}**, Computation method. Other Parameters ---------------- observer : unicode, optional {:func:`colour.colorimetry.whiteness_CIE2004`}, **{'CIE 1931 2 Degree Standard Observer', 'CIE 1964 10 Degree Standard Observer'}**, *CIE Standard Observer* used for computations, *tint* :math:`T` or :math:`T_{10}` value is dependent on viewing field angular subtense. Returns ------- numeric or ndarray *whiteness* :math:`W`. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ +------------+-----------------------+-----------------+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ | ``XYZ_0`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``W`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004k`, :cite:`Wyszecki2000ba`, :cite:`X-Rite2012a`, :cite:`Wikipedia2004b` Examples -------- >>> import numpy as np >>> from colour.models import xyY_to_XYZ >>> XYZ = xyY_to_XYZ(np.array([0.3167, 0.3334, 100])) >>> XYZ_0 = xyY_to_XYZ(np.array([0.3139, 0.3311, 100])) >>> whiteness(XYZ, XYZ_0) # doctest: +ELLIPSIS array([ 93.85..., -1.305...]) >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595]) >>> whiteness(XYZ, XYZ_0, method='Taube 1960') # doctest: +ELLIPSIS 91.4071738... """ kwargs.update({'XYZ': XYZ, 'XYZ_0': XYZ_0}) function = WHITENESS_METHODS.get(method) if function is whiteness_Stensby1968: from colour.models import XYZ_to_Lab, XYZ_to_xy if get_domain_range_scale() == 'reference': XYZ = XYZ / 100 XYZ_0 = XYZ_0 / 100 kwargs.update({'Lab': XYZ_to_Lab(XYZ, XYZ_to_xy(XYZ_0))}) elif function in (whiteness_Ganz1979, whiteness_CIE2004): from colour.models import XYZ_to_xy _X_0, Y_0, _Z_0 = tsplit(XYZ_0) kwargs.update({ 'xy': XYZ_to_xy(XYZ), 'Y': Y_0, 'xy_n': XYZ_to_xy(XYZ_0) }) return function(**filter_kwargs(function, **kwargs))
def lightness(Y, method='CIE 1976', **kwargs): """ Returns the *Lightness* :math:`L` of given *luminance* :math:`Y` using given method. Parameters ---------- Y : numeric or array_like *luminance* :math:`Y`. method : unicode, optional **{'CIE 1976', 'Glasser 1958', 'Wyszecki 1963', 'Fairchild 2010', 'Fairchild 2011'}**, Computation method. Other Parameters ---------------- Y_n : numeric or array_like, optional {:func:`colour.colorimetry.lightness_CIE1976`}, White reference *luminance* :math:`Y_n`. epsilon : numeric or array_like, optional {:func:`colour.colorimetry.lightness_Fairchild2010`, :func:`colour.colorimetry.lightness_Fairchild2011`}, :math:`\\epsilon` exponent. 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:`CIETC1-482004m`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, :cite:`Glasser1958a`, :cite:`Wikipedia2007c`, :cite:`Wyszecki1963b`, :cite:`Wyszecki2000bd` Examples -------- >>> lightness(12.19722535) # doctest: +ELLIPSIS 41.5278758... >>> lightness(12.19722535, Y_n=100) # doctest: +ELLIPSIS 41.5278758... >>> lightness(12.19722535, Y_n=95) # doctest: +ELLIPSIS 42.5199307... >>> lightness(12.19722535, method='Glasser 1958') # doctest: +ELLIPSIS 39.8351264... >>> lightness(12.19722535, method='Wyszecki 1963') # doctest: +ELLIPSIS 40.5475745... >>> lightness(12.19722535, epsilon=0.710, method='Fairchild 2011') ... # doctest: +ELLIPSIS 29.8295108... """ Y = as_float_array(Y) function = LIGHTNESS_METHODS[method] domain_range_reference = get_domain_range_scale() == 'reference' domain_1 = (lightness_Fairchild2010, lightness_Fairchild2011) if function in domain_1 and domain_range_reference: Y = Y / 100 return function(Y, **filter_kwargs(function, **kwargs))
def UCS_Li2017_to_XYZ(Jpapbp: ArrayLike, coefficients: ArrayLike, **kwargs: Any) -> NDArray: """ Convert from one of the *Li et al. (2017)* *CAM16-LCD*, *CAM16-SCD*, or *CAM16-UCS* colourspaces :math:`J'a'b'` array to *CIE XYZ* tristimulus values. Parameters ---------- Jpapbp *Li et al. (2017)* *CAM16-LCD*, *CAM16-SCD*, or *CAM16-UCS* colourspaces :math:`J'a'b'` array. coefficients Coefficients of one of the *Li et al. (2017)* *CAM16-LCD*, *CAM16-SCD*, or *CAM16-UCS* colourspaces. Other Parameters ---------------- kwargs {:func:`colour.CAM16_to_XYZ`}, See the documentation of the previously listed definition. The default viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about 20% of a white object in the scene. Returns ------- :class:`numpy.ndarray` *CIE XYZ* tristimulus values. Warnings -------- The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be given in the same domain-range scale than the ``XYZ`` parameter. Notes ----- +------------+------------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+==================+ | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | | | | | | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | | | | | | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | +------------+------------------------+------------------+ +------------+------------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+==================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+------------------------+------------------+ Examples -------- >>> import numpy as np >>> Jpapbp = np.array([46.06586037, 41.07586491, 14.51025828]) >>> UCS_Li2017_to_XYZ( ... Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) ... # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) >>> from colour.appearance import CAM_KWARGS_CIECAM02_sRGB >>> XYZ_w = CAM_KWARGS_CIECAM02_sRGB['XYZ_w'] >>> UCS_Li2017_to_XYZ( ... Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD'], XYZ_w=XYZ_w / 100) ... # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) """ from colour.appearance import ( CAM_KWARGS_CIECAM02_sRGB, CAM_Specification_CAM16, CAM16_to_XYZ, ) domain_range_reference = get_domain_range_scale() == "reference" settings = CAM_KWARGS_CIECAM02_sRGB.copy() settings.update(**kwargs) XYZ_w = kwargs.get("XYZ_w") if XYZ_w is not None and domain_range_reference: settings["XYZ_w"] = XYZ_w * 100 J, M, h = tsplit(UCS_Li2017_to_JMh_CAM16(Jpapbp, coefficients)) specification = CAM_Specification_CAM16(J=J, M=M, h=h) XYZ = CAM16_to_XYZ(specification, **settings) if domain_range_reference: XYZ /= 100 return XYZ
def XYZ_to_UCS_Luo2006(XYZ: ArrayLike, coefficients: ArrayLike, **kwargs: Any) -> NDArray: """ Convert from *CIE XYZ* tristimulus values to one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. Parameters ---------- XYZ *CIE XYZ* tristimulus values. coefficients Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces. Other Parameters ---------------- kwargs {:func:`colour.XYZ_to_CIECAM02`}, See the documentation of the previously listed definition. The default viewing conditions are that of *IEC 61966-2-1:1999*, i.e. *sRGB* 64 Lux ambient illumination, 80 :math:`cd/m^2`, adapting field luminance about 20% of a white object in the scene. Returns ------- :class:`numpy.ndarray` *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. Warnings -------- The ``XYZ_w`` parameter for :func:`colour.XYZ_to_CAM16` definition must be given in the same domain-range scale than the ``XYZ`` parameter. Notes ----- +------------+------------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+==================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+------------------------+------------------+ +------------+------------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+==================+ | ``Jpapbp`` | ``Jp`` : [0, 100] | ``Jp`` : [0, 1] | | | | | | | ``ap`` : [-100, 100] | ``ap`` : [-1, 1] | | | | | | | ``bp`` : [-100, 100] | ``bp`` : [-1, 1] | +------------+------------------------+------------------+ Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_UCS_Luo2006(XYZ, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) ... # doctest: +ELLIPSIS array([ 46.6138615..., 39.3576023..., 15.9673043...]) """ from colour.appearance import CAM_KWARGS_CIECAM02_sRGB, XYZ_to_CIECAM02 domain_range_reference = get_domain_range_scale() == "reference" settings = CAM_KWARGS_CIECAM02_sRGB.copy() settings.update(**kwargs) XYZ_w = kwargs.get("XYZ_w") if XYZ_w is not None and domain_range_reference: settings["XYZ_w"] = XYZ_w * 100 if domain_range_reference: XYZ = as_float_array(XYZ) * 100 specification = XYZ_to_CIECAM02(XYZ, **settings) JMh = tstack([specification.J, specification.M, specification.h]) return JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients)
def lightness(Y, method='CIE 1976', **kwargs): """ Returns the *Lightness* :math:`L` of given *luminance* :math:`Y` using given method. Parameters ---------- Y : numeric or array_like *luminance* :math:`Y`. method : unicode, optional **{'CIE 1976', 'Glasser 1958', 'Wyszecki 1963', 'Fairchild 2010', 'Fairchild 2011'}**, Computation method. Other Parameters ---------------- Y_n : numeric or array_like, optional {:func:`colour.colorimetry.lightness_CIE1976`}, White reference *luminance* :math:`Y_n`. epsilon : numeric or array_like, optional {:func:`colour.colorimetry.lightness_Fairchild2010`, :func:`colour.colorimetry.lightness_Fairchild2011`}, :math:`\\epsilon` exponent. 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:`CIETC1-482004m`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, :cite:`Glasser1958a`, :cite:`Wikipedia2007c`, :cite:`Wyszecki1963b`, :cite:`Wyszecki2000bd` Examples -------- >>> lightness(12.19722535) # doctest: +ELLIPSIS 41.5278758... >>> lightness(12.19722535, Y_n=100) # doctest: +ELLIPSIS 41.5278758... >>> lightness(12.19722535, Y_n=95) # doctest: +ELLIPSIS 42.5199307... >>> lightness(12.19722535, method='Glasser 1958') # doctest: +ELLIPSIS 39.8351264... >>> lightness(12.19722535, method='Wyszecki 1963') # doctest: +ELLIPSIS 40.5475745... >>> lightness(12.19722535, epsilon=0.710, method='Fairchild 2011') ... # doctest: +ELLIPSIS 29.8295108... """ Y = as_float_array(Y) function = LIGHTNESS_METHODS[method] domain_range_reference = get_domain_range_scale() == 'reference' domain_1 = (lightness_Fairchild2010, lightness_Fairchild2011) if function in domain_1 and domain_range_reference: Y = Y / 100 return function(Y, **filter_kwargs(function, **kwargs))
def chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method='Von Kries', **kwargs): """ Adapts given stimulus from test viewing conditions to reference viewing conditions. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of 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. method : unicode, optional **{'Von Kries', 'CIE 1994', 'CMCCAT2000', 'Fairchild 1990'}**, Computation method. Other Parameters ---------------- E_o1 : numeric {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Test illuminance :math:`E_{o1}` in :math:`cd/m^2`. E_o2 : numeric {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Reference illuminance :math:`E_{o2}` in :math:`cd/m^2`. L_A1 : numeric or array_like {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Luminance of test adapting field :math:`L_{A1}` in :math:`cd/m^2`. L_A2 : numeric or array_like {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Luminance of reference adapting field :math:`L_{A2}` in :math:`cd/m^2`. Y_n : numeric or array_like {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. Y_o : numeric {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Luminance factor :math:`Y_o` of achromatic background normalised to domain [0.18, 1] in **'Reference'** domain-range scale. direction : unicode, optional {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, **{'Forward', 'Reverse'}**, Chromatic adaptation direction. discount_illuminant : bool, optional {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, Truth value indicating if the illuminant should be discounted. n : numeric, optional {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Noise component in fundamental primary system. surround : CMCCAT2000_InductionFactors, optional {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Surround viewing conditions induction factors. transform : unicode, optional {:func:`colour.adaptation.chromatic_adaptation_VonKries`}, **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco', 'Bianco PC'}**, Chromatic adaptation transform. Returns ------- ndarray *CIE XYZ_c* tristimulus values of the stimulus corresponding colour. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_w`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wr`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``Y_o`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_c`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`CIETC1-321994b`, :cite:`Fairchild1991a`, :cite:`Fairchild2013s`, :cite:`Fairchild2013t`, :cite:`Li2002a`, :cite:`Westland2012k` Examples -------- *Von Kries* chromatic adaptation: >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> chromatic_adaptation(XYZ, XYZ_w, XYZ_wr) ... # doctest: +ELLIPSIS array([ 0.2163881..., 0.1257 , 0.0384749...]) *CIE 1994* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.2800, 0.2126, 0.0527]) >>> XYZ_w = np.array([1.09867452, 1.00000000, 0.35591556]) >>> XYZ_wr = np.array([0.95045593, 1.00000000, 1.08905775]) >>> Y_o = 0.20 >>> E_o = 1000 >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='CIE 1994', Y_o=Y_o, E_o1=E_o, E_o2=E_o) ... # doctest: +ELLIPSIS array([ 0.2403379..., 0.2115621..., 0.1764301...]) *CMCCAT2000* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.2248, 0.2274, 0.0854]) >>> XYZ_w = np.array([1.1115, 1.0000, 0.3520]) >>> XYZ_wr = np.array([0.9481, 1.0000, 1.0730]) >>> L_A = 200 >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='CMCCAT2000', L_A1=L_A, L_A2=L_A) ... # doctest: +ELLIPSIS array([ 0.1952698..., 0.2306834..., 0.2497175...]) *Fairchild (1990)* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.1953, 0.2307, 0.2497]) >>> Y_n = 200 >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='Fairchild 1990', Y_n=Y_n) ... # doctest: +ELLIPSIS array([ 0.2332526..., 0.2332455..., 0.7611593...]) """ function = CHROMATIC_ADAPTATION_METHODS[method] domain_range_reference = get_domain_range_scale() == 'reference' domain_100 = (chromatic_adaptation_CIE1994, chromatic_adaptation_CMCCAT2000, chromatic_adaptation_Fairchild1990) if function in domain_100 and domain_range_reference: XYZ = as_float_array(XYZ) * 100 XYZ_w = as_float_array(XYZ_w) * 100 XYZ_wr = as_float_array(XYZ_wr) * 100 if kwargs.get('Y_o'): kwargs['Y_o'] = kwargs['Y_o'] * 100 kwargs.update({'XYZ_w': XYZ_w, 'XYZ_wr': XYZ_wr}) if function is chromatic_adaptation_CIE1994: from colour import XYZ_to_xy kwargs.update({'xy_o1': XYZ_to_xy(XYZ_w), 'xy_o2': XYZ_to_xy(XYZ_wr)}) elif function is chromatic_adaptation_Fairchild1990: kwargs.update({'XYZ_n': XYZ_w, 'XYZ_r': XYZ_wr}) XYZ_c = function(XYZ, **filter_kwargs(function, **kwargs)) if function in domain_100 and domain_range_reference: XYZ_c /= 100 return XYZ_c
def whiteness( XYZ: ArrayLike, XYZ_0: ArrayLike, method: Union[Literal["ASTM E313", "CIE 2004", "Berger 1959", "Ganz 1979", "Stensby 1968", "Taube 1960", ], str, ] = "CIE 2004", **kwargs: Any, ) -> FloatingOrNDArray: """ Return the *whiteness* :math:`W` using given method. Parameters ---------- XYZ *CIE XYZ* tristimulus values of the sample. XYZ_0 *CIE XYZ* tristimulus values of the reference white. method Computation method. Other Parameters ---------------- observer {:func:`colour.colorimetry.whiteness_CIE2004`}, *CIE Standard Observer* used for computations, *tint* :math:`T` or :math:`T_{10}` value is dependent on viewing field angular subtense. Returns ------- :class:`np.floating` or :class:`numpy.ndarray` *Whiteness* :math:`W`. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ +------------+-----------------------+-----------------+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ | ``XYZ_0`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``W`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004k`, :cite:`Wyszecki2000ba`, :cite:`X-Rite2012a`, :cite:`Wikipedia2004b` Examples -------- >>> import numpy as np >>> from colour.models import xyY_to_XYZ >>> XYZ = xyY_to_XYZ(np.array([0.3167, 0.3334, 100])) >>> XYZ_0 = xyY_to_XYZ(np.array([0.3139, 0.3311, 100])) >>> whiteness(XYZ, XYZ_0) # doctest: +ELLIPSIS array([ 93.85..., -1.305...]) >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595]) >>> whiteness(XYZ, XYZ_0, method='Taube 1960') # doctest: +ELLIPSIS 91.4071738... """ XYZ = as_float_array(XYZ) XYZ_0 = as_float_array(XYZ_0) method = validate_method(method, WHITENESS_METHODS) kwargs.update({"XYZ": XYZ, "XYZ_0": XYZ_0}) function = WHITENESS_METHODS[method] if function is whiteness_Stensby1968: from colour.models import XYZ_to_Lab, XYZ_to_xy if get_domain_range_scale() == "reference": XYZ = XYZ / 100 XYZ_0 = XYZ_0 / 100 kwargs.update({"Lab": XYZ_to_Lab(XYZ, XYZ_to_xy(XYZ_0))}) elif function in (whiteness_Ganz1979, whiteness_CIE2004): from colour.models import XYZ_to_xy _X_0, Y_0, _Z_0 = tsplit(XYZ_0) kwargs.update({ "xy": XYZ_to_xy(XYZ), "Y": Y_0, "xy_n": XYZ_to_xy(XYZ_0) }) return function(**filter_kwargs(function, **kwargs))
def chromatic_adaptation( XYZ: ArrayLike, XYZ_w: ArrayLike, XYZ_wr: ArrayLike, method: Union[Literal["CIE 1994", "CMCCAT2000", "Fairchild 1990", "Zhai 2018", "Von Kries", ], str, ] = "Von Kries", **kwargs: Any, ) -> NDArray: """ Adapt given stimulus from test viewing conditions to reference viewing conditions. Parameters ---------- XYZ *CIE XYZ* tristimulus values of stimulus to adapt. XYZ_w Test viewing condition *CIE XYZ* tristimulus values of the whitepoint. XYZ_wr Reference viewing condition *CIE XYZ* tristimulus values of the whitepoint. method Computation method. Other Parameters ---------------- E_o1 {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Test illuminance :math:`E_{o1}` in :math:`cd/m^2`. E_o2 {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Reference illuminance :math:`E_{o2}` in :math:`cd/m^2`. n {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Noise component in fundamental primary system. Y_o {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Luminance factor :math:`Y_o` of achromatic background normalised to domain [0.18, 1] in **'Reference'** domain-range scale. direction {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Chromatic adaptation direction. L_A1 {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Luminance of test adapting field :math:`L_{A1}` in :math:`cd/m^2`. L_A2 {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Luminance of reference adapting field :math:`L_{A2}` in :math:`cd/m^2`. surround {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Surround viewing conditions induction factors. discount_illuminant {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, Truth value indicating if the illuminant should be discounted. Y_n {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. D_b {:func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, Degree of adaptation :math:`D_\\beta` of input illuminant :math:`\\beta`. D_d {:func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, Degree of adaptation :math:`D_\\Delta` of output illuminant :math:`\\Delta`. transform {:func:`colour.adaptation.chromatic_adaptation_VonKries`, :func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, Chromatic adaptation transform. XYZ_wo {:func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, Baseline illuminant (:math:`BI`) :math:`o`. Returns ------- :class:`numpy.ndarray` *CIE XYZ_c* tristimulus values of the stimulus corresponding colour. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_w`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wr`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wo`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``Y_o`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_c`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`CIETC1-321994b`, :cite:`Fairchild1991a`, :cite:`Fairchild2013s`, :cite:`Fairchild2013t`, :cite:`Li2002a`, :cite:`Westland2012k` Examples -------- *Von Kries* chromatic adaptation: >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> chromatic_adaptation(XYZ, XYZ_w, XYZ_wr) ... # doctest: +ELLIPSIS array([ 0.2163881..., 0.1257 , 0.0384749...]) *CIE 1994* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.2800, 0.2126, 0.0527]) >>> XYZ_w = np.array([1.09867452, 1.00000000, 0.35591556]) >>> XYZ_wr = np.array([0.95045593, 1.00000000, 1.08905775]) >>> Y_o = 0.20 >>> E_o = 1000 >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='CIE 1994', Y_o=Y_o, E_o1=E_o, E_o2=E_o) ... # doctest: +ELLIPSIS array([ 0.2403379..., 0.2115621..., 0.1764301...]) *CMCCAT2000* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.2248, 0.2274, 0.0854]) >>> XYZ_w = np.array([1.1115, 1.0000, 0.3520]) >>> XYZ_wr = np.array([0.9481, 1.0000, 1.0730]) >>> L_A = 200 >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='CMCCAT2000', L_A1=L_A, L_A2=L_A) ... # doctest: +ELLIPSIS array([ 0.1952698..., 0.2306834..., 0.2497175...]) *Fairchild (1990)* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.1953, 0.2307, 0.2497]) >>> Y_n = 200 >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='Fairchild 1990', Y_n=Y_n) ... # doctest: +ELLIPSIS array([ 0.2332526..., 0.2332455..., 0.7611593...]) *Zhai and Luo (2018)* chromatic adaptation: >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method='Zhai 2018') ... # doctest: +ELLIPSIS array([ 0.2163881..., 0.1257 , 0.0384749...]) >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='Zhai 2018', D_b=0.9, ... XYZ_wo=np.array([100, 100, 100])) ... # doctest: +ELLIPSIS array([ 0.2152436..., 0.1253522..., 0.0388406...]) """ method = validate_method(method, CHROMATIC_ADAPTATION_METHODS) function = CHROMATIC_ADAPTATION_METHODS[method] domain_range_reference = get_domain_range_scale() == "reference" domain_100 = ( chromatic_adaptation_CIE1994, chromatic_adaptation_CMCCAT2000, chromatic_adaptation_Fairchild1990, chromatic_adaptation_Zhai2018, ) if function in domain_100 and domain_range_reference: XYZ = as_float_array(XYZ) * 100 XYZ_w = as_float_array(XYZ_w) * 100 XYZ_wr = as_float_array(XYZ_wr) * 100 if "Y_o" in kwargs: kwargs["Y_o"] = kwargs["Y_o"] * 100 if "XYZ_wo" in kwargs: kwargs["XYZ_wo"] = kwargs["XYZ_wo"] * 100 kwargs.update({"XYZ_w": XYZ_w, "XYZ_wr": XYZ_wr}) if function is chromatic_adaptation_CIE1994: from colour import XYZ_to_xy kwargs.update({"xy_o1": XYZ_to_xy(XYZ_w), "xy_o2": XYZ_to_xy(XYZ_wr)}) elif function is chromatic_adaptation_Fairchild1990: kwargs.update({"XYZ_n": XYZ_w, "XYZ_r": XYZ_wr}) elif function is chromatic_adaptation_Zhai2018: kwargs.update({"XYZ_wb": XYZ_w, "XYZ_wd": XYZ_wr}) XYZ_c = function(XYZ, **filter_kwargs(function, **kwargs)) if function in domain_100 and domain_range_reference: XYZ_c /= 100 return XYZ_c
def luminance(LV, method='CIE 1976', **kwargs): """ Returns the *luminance* :math:`Y` of given *Lightness* :math:`L^*` or given *Munsell* value :math:`V`. Parameters ---------- LV : numeric or array_like *Lightness* :math:`L^*` or *Munsell* value :math:`V`. method : unicode, optional **{'CIE 1976', 'Newhall 1943', 'ASTM D1535-08', 'Fairchild 2010', 'Fairchild 2011'}**, Computation method. Other Parameters ---------------- Y_n : numeric or array_like, optional {:func:`colour.colorimetry.luminance_CIE1976`}, White reference *luminance* :math:`Y_n`. epsilon : numeric or array_like, optional {:func:`colour.colorimetry.lightness_Fairchild2010`, :func:`colour.colorimetry.lightness_Fairchild2011`}, :math:`\\epsilon` exponent. Returns ------- numeric or array_like *luminance* :math:`Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``LV`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`ASTMInternational2008a`, :cite:`CIETC1-482004m`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, :cite:`Newhall1943a`, :cite:`Wikipedia2001b`, :cite:`Wyszecki2000bd` Examples -------- >>> luminance(41.527875844653451) # doctest: +ELLIPSIS 12.1972253... >>> luminance(41.527875844653451, Y_n=100) # doctest: +ELLIPSIS 12.1972253... >>> luminance(42.51993072812094, Y_n=95) # doctest: +ELLIPSIS 12.1972253... >>> luminance(4.08244375 * 10, method='Newhall 1943') ... # doctest: +ELLIPSIS 12.5500788... >>> luminance(4.08244375 * 10, method='ASTM D1535-08') ... # doctest: +ELLIPSIS 12.2363426... >>> luminance(29.829510892279330, epsilon=0.710, method='Fairchild 2011') ... # doctest: +ELLIPSIS 12.1972253... """ function = LUMINANCE_METHODS[method] domain_range_reference = get_domain_range_scale() == 'reference' domain_1 = (luminance_Fairchild2010, luminance_Fairchild2011) domain_10 = (luminance_Newhall1943, luminance_ASTMD153508) if function in domain_10 and domain_range_reference: LV /= 10 Y_V = function(LV, **filter_kwargs(function, **kwargs)) if function in domain_1 and domain_range_reference: Y_V *= 100 return Y_V
def whiteness(XYZ, XYZ_0, method='CIE 2004', **kwargs): """ Returns the *whiteness* :math:`W` using given method. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of sample. XYZ_0 : array_like *CIE XYZ* tristimulus values of reference white. method : unicode, optional **{'CIE 2004', 'Berger 1959', 'Taube 1960', 'Stensby 1968', 'ASTM E313', 'Ganz 1979'}**, Computation method. Other Parameters ---------------- observer : unicode, optional {:func:`colour.colorimetry.whiteness_CIE2004`}, **{'CIE 1931 2 Degree Standard Observer', 'CIE 1964 10 Degree Standard Observer'}**, *CIE Standard Observer* used for computations, *tint* :math:`T` or :math:`T_{10}` value is dependent on viewing field angular subtense. Returns ------- numeric or ndarray *whiteness* :math:`W`. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ +------------+-----------------------+-----------------+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ | ``XYZ_0`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``W`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004k`, :cite:`Wyszecki2000ba`, :cite:`X-Rite2012a`, :cite:`Wikipedia2004b` Examples -------- >>> import numpy as np >>> from colour.models import xyY_to_XYZ >>> XYZ = xyY_to_XYZ(np.array([0.3167, 0.3334, 100])) >>> XYZ_0 = xyY_to_XYZ(np.array([0.3139, 0.3311, 100])) >>> whiteness(XYZ, XYZ_0) # doctest: +ELLIPSIS array([ 93.85..., -1.305...]) >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000]) >>> XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595]) >>> whiteness(XYZ, XYZ_0, method='Taube 1960') # doctest: +ELLIPSIS 91.4071738... """ kwargs.update({'XYZ': XYZ, 'XYZ_0': XYZ_0}) function = WHITENESS_METHODS.get(method) if function is whiteness_Stensby1968: from colour.models import XYZ_to_Lab, XYZ_to_xy if get_domain_range_scale() == 'reference': XYZ = XYZ / 100 XYZ_0 = XYZ_0 / 100 kwargs.update({'Lab': XYZ_to_Lab(XYZ, XYZ_to_xy(XYZ_0))}) elif function in (whiteness_Ganz1979, whiteness_CIE2004): from colour.models import XYZ_to_xy _X_0, Y_0, _Z_0 = tsplit(XYZ_0) kwargs.update({ 'xy': XYZ_to_xy(XYZ), 'Y': Y_0, 'xy_n': XYZ_to_xy(XYZ_0) }) return function(**filter_kwargs(function, **kwargs))
def chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method='Von Kries', **kwargs): """ Adapts given stimulus from test viewing conditions to reference viewing conditions. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of 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. method : unicode, optional **{'Von Kries', 'CIE 1994', 'CMCCAT2000', 'Fairchild 1990'}**, Computation method. Other Parameters ---------------- E_o1 : numeric {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Test illuminance :math:`E_{o1}` in :math:`cd/m^2`. E_o2 : numeric {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Reference illuminance :math:`E_{o2}` in :math:`cd/m^2`. L_A1 : numeric or array_like {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Luminance of test adapting field :math:`L_{A1}` in :math:`cd/m^2`. L_A2 : numeric or array_like {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Luminance of reference adapting field :math:`L_{A2}` in :math:`cd/m^2`. Y_n : numeric or array_like {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. Y_o : numeric {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Luminance factor :math:`Y_o` of achromatic background normalised to domain [0.18, 1] in **'Reference'** domain-range scale. direction : unicode, optional {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, **{'Forward', 'Reverse'}**, Chromatic adaptation direction. discount_illuminant : bool, optional {:func:`colour.adaptation.chromatic_adaptation_Fairchild1990`}, Truth value indicating if the illuminant should be discounted. n : numeric, optional {:func:`colour.adaptation.chromatic_adaptation_CIE1994`}, Noise component in fundamental primary system. surround : CMCCAT2000_InductionFactors, optional {:func:`colour.adaptation.chromatic_adaptation_CMCCAT2000`}, Surround viewing conditions induction factors. transform : unicode, optional {:func:`colour.adaptation.chromatic_adaptation_VonKries`}, **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco', 'Bianco PC'}**, Chromatic adaptation transform. Returns ------- ndarray *CIE XYZ_c* tristimulus values of the stimulus corresponding colour. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_w`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wr`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``Y_o`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_c`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`CIETC1-321994b`, :cite:`Fairchild1991a`, :cite:`Fairchild2013s`, :cite:`Fairchild2013t`, :cite:`Li2002a`, :cite:`Westland2012k` Examples -------- *Von Kries* chromatic adaptation: >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> chromatic_adaptation(XYZ, XYZ_w, XYZ_wr) ... # doctest: +ELLIPSIS array([ 0.2163881..., 0.1257 , 0.0384749...]) *CIE 1994* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.2800, 0.2126, 0.0527]) >>> XYZ_w = np.array([1.09867452, 1.00000000, 0.35591556]) >>> XYZ_wr = np.array([0.95045593, 1.00000000, 1.08905775]) >>> Y_o = 0.20 >>> E_o = 1000 >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='CIE 1994', Y_o=Y_o, E_o1=E_o, E_o2=E_o) ... # doctest: +ELLIPSIS array([ 0.2403379..., 0.2115621..., 0.1764301...]) *CMCCAT2000* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.2248, 0.2274, 0.0854]) >>> XYZ_w = np.array([1.1115, 1.0000, 0.3520]) >>> XYZ_wr = np.array([0.9481, 1.0000, 1.0730]) >>> L_A = 200 >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='CMCCAT2000', L_A1=L_A, L_A2=L_A) ... # doctest: +ELLIPSIS array([ 0.1952698..., 0.2306834..., 0.2497175...]) *Fairchild (1990)* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.1953, 0.2307, 0.2497]) >>> Y_n = 200 >>> chromatic_adaptation( ... XYZ, XYZ_w, XYZ_wr, method='Fairchild 1990', Y_n=Y_n) ... # doctest: +ELLIPSIS array([ 0.2332526..., 0.2332455..., 0.7611593...]) """ function = CHROMATIC_ADAPTATION_METHODS[method] domain_range_reference = get_domain_range_scale() == 'reference' domain_100 = (chromatic_adaptation_CIE1994, chromatic_adaptation_CMCCAT2000, chromatic_adaptation_Fairchild1990) if function in domain_100 and domain_range_reference: XYZ = as_float_array(XYZ) * 100 XYZ_w = as_float_array(XYZ_w) * 100 XYZ_wr = as_float_array(XYZ_wr) * 100 if kwargs.get('Y_o'): kwargs['Y_o'] = kwargs['Y_o'] * 100 kwargs.update({'XYZ_w': XYZ_w, 'XYZ_wr': XYZ_wr}) if function is chromatic_adaptation_CIE1994: from colour import XYZ_to_xy kwargs.update({'xy_o1': XYZ_to_xy(XYZ_w), 'xy_o2': XYZ_to_xy(XYZ_wr)}) elif function is chromatic_adaptation_Fairchild1990: kwargs.update({'XYZ_n': XYZ_w, 'XYZ_r': XYZ_wr}) XYZ_c = function(XYZ, **filter_kwargs(function, **kwargs)) if function in domain_100 and domain_range_reference: XYZ_c /= 100 return XYZ_c
def lightness( Y: FloatingOrArrayLike, method: Union[ Literal[ "Abebe 2017", "CIE 1976", "Glasser 1958", "Fairchild 2010", "Fairchild 2011", "Wyszecki 1963", ], str, ] = "CIE 1976", **kwargs: Any, ) -> FloatingOrNDArray: """ Return the *Lightness* :math:`L` of given *luminance* :math:`Y` using given method. Parameters ---------- Y *Luminance* :math:`Y`. method Computation method. Other Parameters ---------------- Y_n {:func:`colour.colorimetry.lightness_Abebe2017`, :func:`colour.colorimetry.lightness_CIE1976`}, White reference *luminance* :math:`Y_n`. epsilon {:func:`colour.colorimetry.lightness_Fairchild2010`, :func:`colour.colorimetry.lightness_Fairchild2011`}, :math:`\\epsilon` exponent. 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:`Abebe2017a`, :cite:`CIETC1-482004m`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, :cite:`Glasser1958a`, :cite:`Wikipedia2007c`, :cite:`Wyszecki1963b`, :cite:`Wyszecki2000bd` Examples -------- >>> lightness(12.19722535) # doctest: +ELLIPSIS 41.5278758... >>> lightness(12.19722535, Y_n=100) # doctest: +ELLIPSIS 41.5278758... >>> lightness(12.19722535, Y_n=95) # doctest: +ELLIPSIS 42.5199307... >>> lightness(12.19722535, method='Glasser 1958') # doctest: +ELLIPSIS 39.8351264... >>> lightness(12.19722535, method='Wyszecki 1963') # doctest: +ELLIPSIS 40.5475745... >>> lightness(12.19722535, epsilon=0.710, method='Fairchild 2011') ... # doctest: +ELLIPSIS 29.8295108... >>> lightness(12.19722535, epsilon=0.710, method='Fairchild 2011') ... # doctest: +ELLIPSIS 29.8295108... >>> lightness(12.19722535, method='Abebe 2017') ... # doctest: +ELLIPSIS 48.6955571... """ Y = as_float_array(Y) method = validate_method(method, LIGHTNESS_METHODS) function = LIGHTNESS_METHODS[method] # NOTE: "Abebe et al. (2017)" uses absolute luminance levels and has # undefined domain-range scale, yet we modify its behaviour consistency # with the other methods. domain_range_reference = get_domain_range_scale() == "reference" domain_range_1 = get_domain_range_scale() == "1" domain_range_100 = get_domain_range_scale() == "100" domain_1 = (lightness_Fairchild2010, lightness_Fairchild2011) domain_undefined = (lightness_Abebe2017,) if function in domain_1 and domain_range_reference: Y = Y / 100 if function in domain_undefined and domain_range_1: Y = Y * 100 L = function(Y, **filter_kwargs(function, **kwargs)) if function in domain_undefined and ( domain_range_reference or domain_range_100 ): L *= 100 return L
def luminance( LV: FloatingOrArrayLike, method: Union[Literal["Abebe 2017", "CIE 1976", "Glasser 1958", "Fairchild 2010", "Fairchild 2011", "Wyszecki 1963", ], str, ] = "CIE 1976", **kwargs: Any, ) -> FloatingOrNDArray: """ Return the *luminance* :math:`Y` of given *Lightness* :math:`L^*` or given *Munsell* value :math:`V`. Parameters ---------- LV *Lightness* :math:`L^*` or *Munsell* value :math:`V`. method Computation method. Other Parameters ---------------- Y_n {:func:`colour.colorimetry.luminance_Abebe2017`, :func:`colour.colorimetry.luminance_CIE1976`}, White reference *luminance* :math:`Y_n`. epsilon {:func:`colour.colorimetry.lightness_Fairchild2010`, :func:`colour.colorimetry.lightness_Fairchild2011`}, :math:`\\epsilon` exponent. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *Luminance* :math:`Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``LV`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Abebe2017a`, :cite:`ASTMInternational2008a`, :cite:`CIETC1-482004m`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, :cite:`Newhall1943a`, :cite:`Wikipedia2001b`, :cite:`Wyszecki2000bd` Examples -------- >>> luminance(41.527875844653451) # doctest: +ELLIPSIS 12.1972253... >>> luminance(41.527875844653451, Y_n=100) # doctest: +ELLIPSIS 12.1972253... >>> luminance(42.51993072812094, Y_n=95) # doctest: +ELLIPSIS 12.1972253... >>> luminance(4.08244375 * 10, method='Newhall 1943') ... # doctest: +ELLIPSIS 12.5500788... >>> luminance(4.08244375 * 10, method='ASTM D1535') ... # doctest: +ELLIPSIS 12.2363426... >>> luminance(29.829510892279330, epsilon=0.710, method='Fairchild 2011') ... # doctest: +ELLIPSIS 12.1972253... """ LV = as_float_array(LV) method = validate_method(method, LUMINANCE_METHODS) function = LUMINANCE_METHODS[method] # NOTE: "Abebe et al. (2017)" uses absolute luminance levels and has # undefined domain-range scale, yet we modify its behaviour consistency # with the other methods. domain_range_reference = get_domain_range_scale() == "reference" domain_range_1 = get_domain_range_scale() == "1" domain_1 = (luminance_Fairchild2010, luminance_Fairchild2011) domain_10 = (luminance_Newhall1943, luminance_ASTMD1535) domain_undefined = (luminance_Abebe2017, ) if function in domain_10 and domain_range_reference: LV = LV / 10 if function in domain_undefined and domain_range_1: LV = LV * 100 Y_V = function(LV, **filter_kwargs(function, **kwargs)) if function in domain_1 and domain_range_reference: Y_V *= 100 if function in domain_undefined and domain_range_1: Y_V /= 100 return Y_V
def luminance(LV, method='CIE 1976', **kwargs): """ Returns the *luminance* :math:`Y` of given *Lightness* :math:`L^*` or given *Munsell* value :math:`V`. Parameters ---------- LV : numeric or array_like *Lightness* :math:`L^*` or *Munsell* value :math:`V`. method : unicode, optional **{'CIE 1976', 'Newhall 1943', 'ASTM D1535-08', 'Fairchild 2010', 'Fairchild 2011'}**, Computation method. Other Parameters ---------------- Y_n : numeric or array_like, optional {:func:`colour.colorimetry.luminance_CIE1976`}, White reference *luminance* :math:`Y_n`. epsilon : numeric or array_like, optional {:func:`colour.colorimetry.lightness_Fairchild2010`, :func:`colour.colorimetry.lightness_Fairchild2011`}, :math:`\\epsilon` exponent. Returns ------- numeric or array_like *luminance* :math:`Y`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``LV`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`ASTMInternational2008a`, :cite:`Fairchild2010`, :cite:`Fairchild2011`, :cite:`Lindbloom2003d`, :cite:`Newhall1943a`, :cite:`Wikipedia2001b`, :cite:`Wyszecki2000bd` Examples -------- >>> luminance(41.527875844653451) # doctest: +ELLIPSIS 12.1972253... >>> luminance(41.527875844653451, Y_n=100) # doctest: +ELLIPSIS 12.1972253... >>> luminance(42.51993072812094, Y_n=95) # doctest: +ELLIPSIS 12.1972253... >>> luminance(4.08244375 * 10, method='Newhall 1943') ... # doctest: +ELLIPSIS 12.5500788... >>> luminance(4.08244375 * 10, method='ASTM D1535-08') ... # doctest: +ELLIPSIS 12.2363426... >>> luminance(29.829510892279330, epsilon=0.710, method='Fairchild 2011') ... # doctest: +ELLIPSIS 12.1972253... """ function = LUMINANCE_METHODS[method] domain_range_reference = get_domain_range_scale() == 'reference' domain_1 = (luminance_Fairchild2010, luminance_Fairchild2011) domain_10 = (luminance_Newhall1943, luminance_ASTMD153508) if function in domain_10 and domain_range_reference: LV /= 10 Y_V = function(LV, **filter_kwargs(function, **kwargs)) if function in domain_1 and domain_range_reference: Y_V *= 100 return Y_V
def multiprocessing_pool(*args: Any, **kwargs: Any) -> Generator: """ Define a context manager providing a multiprocessing pool. Other Parameters ---------------- args Arguments. kwargs Keywords arguments. Yields ------ Generator Multiprocessing pool. Examples -------- >>> from functools import partial >>> def _add(a, b): ... return a + b >>> with multiprocessing_pool() as pool: ... pool.map(partial(_add, b=2), range(10)) ... # doctest: +SKIP [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] """ from colour.utilities import get_domain_range_scale class _DummyPool: """ A dummy multiprocessing pool that does not perform multiprocessing. Other Parameters ---------------- args Arguments. kwargs Keywords arguments. """ def __init__(self, *args: Any, **kwargs: Any): pass def map(self, func, iterable, chunksize=None): """Apply given function to each element of given iterable.""" return [func(a) for a in iterable] def terminate(self): """Terminate the process.""" pass kwargs["initializer"] = _initializer kwargs["initargs"] = ({"scale": get_domain_range_scale()}, ) pool_factory: Callable if _MULTIPROCESSING_ENABLED: pool_factory = multiprocessing.Pool else: pool_factory = _DummyPool pool = pool_factory(*args, **kwargs) try: yield pool finally: pool.terminate()
def test_domain_range_scale(self): """ Tests :func:`colour.utilities.common.domain_range_scale` definition. """ self.assertEqual(get_domain_range_scale(), 'reference') with domain_range_scale('Reference'): self.assertEqual(get_domain_range_scale(), 'reference') self.assertEqual(get_domain_range_scale(), 'reference') with domain_range_scale('1'): self.assertEqual(get_domain_range_scale(), '1') self.assertEqual(get_domain_range_scale(), 'reference') with domain_range_scale('100'): self.assertEqual(get_domain_range_scale(), '100') self.assertEqual(get_domain_range_scale(), 'reference') def _domain_range_change(a): """ Helper definition performing domain-range scale. """ b = to_domain_10(a) b *= 2 return from_range_100(b) with domain_range_scale('Reference'): with domain_range_scale('1'): with domain_range_scale('100'): with domain_range_scale('Ignore'): self.assertEqual(get_domain_range_scale(), 'ignore') self.assertEqual(_domain_range_change(4), 8) self.assertEqual(get_domain_range_scale(), '100') self.assertEqual(_domain_range_change(40), 8) self.assertEqual(get_domain_range_scale(), '1') self.assertEqual(_domain_range_change(0.4), 0.08) self.assertEqual(get_domain_range_scale(), 'reference') self.assertEqual(_domain_range_change(4), 8) self.assertEqual(get_domain_range_scale(), 'reference')
def test_domain_range_scale(self): """ Test :func:`colour.utilities.common.domain_range_scale` definition. """ self.assertEqual(get_domain_range_scale(), "reference") with domain_range_scale("Reference"): self.assertEqual(get_domain_range_scale(), "reference") self.assertEqual(get_domain_range_scale(), "reference") with domain_range_scale("1"): self.assertEqual(get_domain_range_scale(), "1") self.assertEqual(get_domain_range_scale(), "reference") with domain_range_scale("100"): self.assertEqual(get_domain_range_scale(), "100") self.assertEqual(get_domain_range_scale(), "reference") def fn_a(a): """Change the domain-range scale for unit testing.""" b = to_domain_10(a) b *= 2 return from_range_100(b) with domain_range_scale("Reference"): with domain_range_scale("1"): with domain_range_scale("100"): with domain_range_scale("Ignore"): self.assertEqual(get_domain_range_scale(), "ignore") self.assertEqual(fn_a(4), 8) self.assertEqual(get_domain_range_scale(), "100") self.assertEqual(fn_a(40), 8) self.assertEqual(get_domain_range_scale(), "1") self.assertEqual(fn_a(0.4), 0.08) self.assertEqual(get_domain_range_scale(), "reference") self.assertEqual(fn_a(4), 8) self.assertEqual(get_domain_range_scale(), "reference") @domain_range_scale("1") def fn_b(a): """Change the domain-range scale for unit testing.""" b = to_domain_10(a) b *= 2 return from_range_100(b) self.assertEqual(fn_b(10), 2.0)
def test_domain_range_scale(self): """ Tests :func:`colour.utilities.common.domain_range_scale` definition. """ self.assertEqual(get_domain_range_scale(), 'reference') with domain_range_scale('Reference'): self.assertEqual(get_domain_range_scale(), 'reference') self.assertEqual(get_domain_range_scale(), 'reference') with domain_range_scale('1'): self.assertEqual(get_domain_range_scale(), '1') self.assertEqual(get_domain_range_scale(), 'reference') with domain_range_scale('100'): self.assertEqual(get_domain_range_scale(), '100') self.assertEqual(get_domain_range_scale(), 'reference') def fn_a(a): """ Helper definition performing domain-range scale. """ b = to_domain_10(a) b *= 2 return from_range_100(b) with domain_range_scale('Reference'): with domain_range_scale('1'): with domain_range_scale('100'): with domain_range_scale('Ignore'): self.assertEqual(get_domain_range_scale(), 'ignore') self.assertEqual(fn_a(4), 8) self.assertEqual(get_domain_range_scale(), '100') self.assertEqual(fn_a(40), 8) self.assertEqual(get_domain_range_scale(), '1') self.assertEqual(fn_a(0.4), 0.08) self.assertEqual(get_domain_range_scale(), 'reference') self.assertEqual(fn_a(4), 8) self.assertEqual(get_domain_range_scale(), 'reference') @domain_range_scale(1) def fn_b(a): """ Helper definition performing domain-range scale. """ b = to_domain_10(a) b *= 2 return from_range_100(b) self.assertEqual(fn_b(10), 2.0)
def delta_E_DIN99(Lab_1: ArrayLike, Lab_2: ArrayLike, textiles: Boolean = False) -> FloatingOrNDArray: """ Return the difference :math:`\\Delta E_{DIN99}` between two given *CIE L\\*a\\*b\\** colourspace arrays using *DIN99* formula. Parameters ---------- Lab_1 *CIE L\\*a\\*b\\** colourspace array 1. Lab_2 *CIE L\\*a\\*b\\** colourspace array 2. textiles Textiles application specific parametric factors, :math:`k_E=2,\\ k_{CH}=0.5` weights are used instead of :math:`k_E=1,\\ k_{CH}=1`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Colour difference :math:`\\Delta E_{DIN99}`. Notes ----- +------------+-----------------------+-------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===================+ | ``Lab_1`` | ``L_1`` : [0, 100] | ``L_1`` : [0, 1] | | | | | | | ``a_1`` : [-100, 100] | ``a_1`` : [-1, 1] | | | | | | | ``b_1`` : [-100, 100] | ``b_1`` : [-1, 1] | +------------+-----------------------+-------------------+ | ``Lab_2`` | ``L_2`` : [0, 100] | ``L_2`` : [0, 1] | | | | | | | ``a_2`` : [-100, 100] | ``a_2`` : [-1, 1] | | | | | | | ``b_2`` : [-100, 100] | ``b_2`` : [-1, 1] | +------------+-----------------------+-------------------+ References ---------- :cite:`ASTMInternational2007` Examples -------- >>> import numpy as np >>> Lab_1 = np.array([60.2574, -34.0099, 36.2677]) >>> Lab_2 = np.array([60.4626, -34.1751, 39.4387]) >>> delta_E_DIN99(Lab_1, Lab_2) # doctest: +ELLIPSIS 1.1772166... """ k_E = 2 if textiles else 1 k_CH = 0.5 if textiles else 1 factor = 100 if get_domain_range_scale() == "1" else 1 d_E = euclidean_distance( Lab_to_DIN99(Lab_1, k_E, k_CH) * factor, Lab_to_DIN99(Lab_2, k_E, k_CH) * factor, ) return d_E