def ICTCP_to_RGB(ICTCP, L_p=10000): """ Converts from :math:`IC_TC_P` colour encoding to *Rec. 2020* colourspace. Parameters ---------- ICTCP : array_like :math:`IC_TC_P` colour encoding array. L_p : numeric, optional Display peak luminance :math:`cd/m^2` for *SMPTE ST 2084:2014* non-linear encoding. Returns ------- ndarray *Rec. 2020* colourspace array. Examples -------- >>> ICTCP = np.array([0.09554079, -0.00890639, 0.01389286]) >>> ICTCP_to_RGB(ICTCP) # doctest: +ELLIPSIS array([ 0.3518145..., 0.2693475..., 0.2128802...]) """ LMS_p = dot_vector(ICTCP_ICTCP_TO_LMS_P_MATRIX, ICTCP) LMS = eotf_ST2084(LMS_p, L_p) RGB = dot_vector(ICTCP_LMS_TO_RGB_MATRIX, LMS) return RGB
def IPT_to_XYZ(IPT): """ Converts from *IPT* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- IPT : array_like *IPT* colourspace array. Returns ------- ndarray *CIE XYZ* tristimulus values. Examples -------- >>> IPT = np.array([1.00300825, 0.01906918, -0.01369292]) >>> IPT_to_XYZ(IPT) # doctest: +ELLIPSIS array([ 0.9690723..., 1. , 1.1217921...]) """ LMS = dot_vector(IPT_IPT_TO_LMS_MATRIX, IPT) LMS_prime = np.sign(LMS) * np.abs(LMS) ** (1 / 0.43) XYZ = dot_vector(IPT_LMS_TO_XYZ_MATRIX, LMS_prime) return XYZ
def XYZ_to_IPT(XYZ): """ Converts from *CIE XYZ* tristimulus values to *IPT* colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray *IPT* colourspace array. Notes ----- - Input *CIE XYZ* tristimulus values needs to be adapted for *CIE Standard Illuminant D Series* *D65*. Examples -------- >>> XYZ = np.array([0.96907232, 1, 1.12179215]) >>> XYZ_to_IPT(XYZ) # doctest: +ELLIPSIS array([ 1.0030082..., 0.0190691..., -0.0136929...]) """ LMS = dot_vector(IPT_XYZ_TO_LMS_MATRIX, XYZ) LMS_prime = np.sign(LMS) * np.abs(LMS) ** 0.43 IPT = dot_vector(IPT_LMS_TO_IPT_MATRIX, LMS_prime) return IPT
def IPT_to_XYZ(IPT): """ Converts from *IPT* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- IPT : array_like *IPT* colourspace array. Returns ------- ndarray *CIE XYZ* tristimulus values. References ---------- - :cite:`Fairchild2013y` Examples -------- >>> IPT = np.array([1.00300825, 0.01906918, -0.01369292]) >>> IPT_to_XYZ(IPT) # doctest: +ELLIPSIS array([ 0.9690723..., 1. , 1.1217921...]) """ LMS = dot_vector(IPT_IPT_TO_LMS_MATRIX, IPT) LMS_prime = np.sign(LMS) * np.abs(LMS) ** (1 / 0.43) XYZ = dot_vector(IPT_LMS_TO_XYZ_MATRIX, LMS_prime) return XYZ
def RGB_to_ICTCP(RGB, L_p=10000): """ Converts from *Rec. 2020* colourspace to :math:`IC_TC_P` colour encoding. Parameters ---------- RGB : array_like *Rec. 2020* colourspace array. L_p : numeric, optional Display peak luminance :math:`cd/m^2` for *SMPTE ST 2084:2014* non-linear encoding. Returns ------- ndarray :math:`IC_TC_P` colour encoding array. Examples -------- >>> RGB = np.array([0.35181454, 0.26934757, 0.21288023]) >>> RGB_to_ICTCP(RGB) # doctest: +ELLIPSIS array([ 0.0955407..., -0.0089063..., 0.0138928...]) """ LMS = dot_vector(ICTCP_RGB_TO_LMS_MATRIX, RGB) LMS_p = oetf_ST2084(LMS, L_p) ICTCP = dot_vector(ICTCP_LMS_P_TO_ICTCP_MATRIX, LMS_p) return ICTCP
def hdr_IPT_to_XYZ(IPT_hdr, Y_s=0.2, Y_abs=100): """ Converts from *hdr-IPT* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- IPT_hdr : array_like *hdr-IPT* colourspace array. Y_s : numeric or array_like Relative luminance :math:`Y_s` of the surround in domain [0, 1]. Y_abs : numeric or array_like Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. Returns ------- ndarray *CIE XYZ* tristimulus values. Examples -------- >>> IPT_hdr = np.array([94.65929175, 0.38041773, -0.26731187]) >>> hdr_IPT_to_XYZ(IPT_hdr) # doctest: +ELLIPSIS array([ 0.9690723..., 1. , 1.1217921...]) """ e = exponent_hdr_IPT(Y_s, Y_abs)[..., np.newaxis] LMS = dot_vector(IPT_IPT_TO_LMS_MATRIX, IPT_hdr) LMS_prime = np.sign(LMS) * np.abs(luminance_Fairchild2010(LMS, e)) XYZ = dot_vector(IPT_LMS_TO_XYZ_MATRIX, LMS_prime) return XYZ
def XYZ_to_IPT(XYZ): """ Converts from *CIE XYZ* tristimulus values to *IPT* colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray *IPT* colourspace array. Notes ----- - Input *CIE XYZ* tristimulus values needs to be adapted for *CIE Standard Illuminant D Series* *D65*. References ---------- - :cite:`Fairchild2013y` Examples -------- >>> XYZ = np.array([0.96907232, 1.00000000, 1.12179215]) >>> XYZ_to_IPT(XYZ) # doctest: +ELLIPSIS array([ 1.0030082..., 0.0190691..., -0.0136929...]) """ LMS = dot_vector(IPT_XYZ_TO_LMS_MATRIX, XYZ) LMS_prime = np.sign(LMS) * np.abs(LMS) ** 0.43 IPT = dot_vector(IPT_LMS_TO_IPT_MATRIX, LMS_prime) return IPT
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 in domain [0, 1]. 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 ----- - 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.96907232, 1.00000000, 1.12179215]) >>> XYZ_to_hdr_IPT(XYZ) # doctest: +ELLIPSIS array([ 93.5317473..., 1.8564156..., -1.3292254...]) >>> XYZ_to_hdr_IPT(XYZ, method='Fairchild 2010') # doctest: +ELLIPSIS array([ 94.6592917..., 0.3804177..., -0.2673118...]) """ 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) LMS_prime = np.sign(LMS) * np.abs(lightness_callable(LMS, e)) IPT = dot_vector(IPT_LMS_TO_IPT_MATRIX, LMS_prime) return IPT
def RGB_to_ICTCP(RGB, L_p=10000): """ Converts from *ITU-R BT.2020* colourspace to :math:`IC_TC_P` colour encoding. Parameters ---------- RGB : array_like *ITU-R BT.2020* colourspace array. L_p : numeric, optional Display peak luminance :math:`cd/m^2` for *SMPTE ST 2084:2014* non-linear encoding. Returns ------- ndarray :math:`IC_TC_P` colour encoding array. Notes ----- +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``RGB`` | [0, 1] | [0, 1] | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``ICTCP`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``CT`` : [-1, 1] | ``CT`` : [-1, 1] | | | | | | | ``CP`` : [-1, 1] | ``CP`` : [-1, 1] | +------------+-----------------------+------------------+ References ---------- :cite:`Dolby2016a`, :cite:`Lu2016c` Examples -------- >>> RGB = np.array([0.45620519, 0.03081071, 0.04091952]) >>> RGB_to_ICTCP(RGB) # doctest: +ELLIPSIS array([ 0.0735136..., 0.0047525..., 0.0935159...]) """ RGB = to_domain_1(RGB) LMS = dot_vector(ICTCP_RGB_TO_LMS_MATRIX, RGB) with domain_range_scale('ignore'): LMS_p = eotf_inverse_ST2084(LMS, L_p) ICTCP = dot_vector(ICTCP_LMS_P_TO_ICTCP_MATRIX, LMS_p) return from_range_1(ICTCP)
def ICTCP_to_RGB(ICTCP, L_p=10000): """ Converts from :math:`IC_TC_P` colour encoding to *ITU-R BT.2020* colourspace. Parameters ---------- ICTCP : array_like :math:`IC_TC_P` colour encoding array. L_p : numeric, optional Display peak luminance :math:`cd/m^2` for *SMPTE ST 2084:2014* non-linear encoding. Returns ------- ndarray *ITU-R BT.2020* colourspace array. Notes ----- +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``ICTCP`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``CT`` : [-1, 1] | ``CT`` : [-1, 1] | | | | | | | ``CP`` : [-1, 1] | ``CP`` : [-1, 1] | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``RGB`` | [0, 1] | [0, 1] | +------------+-----------------------+------------------+ References ---------- :cite:`Dolby2016a`, :cite:`Lu2016c` Examples -------- >>> ICTCP = np.array([0.07351364, 0.00475253, 0.09351596]) >>> ICTCP_to_RGB(ICTCP) # doctest: +ELLIPSIS array([ 0.4562052..., 0.0308107..., 0.0409195...]) """ ICTCP = to_domain_1(ICTCP) LMS_p = dot_vector(ICTCP_ICTCP_TO_LMS_P_MATRIX, ICTCP) with domain_range_scale('ignore'): LMS = eotf_ST2084(LMS_p, L_p) RGB = dot_vector(ICTCP_LMS_TO_RGB_MATRIX, LMS) return from_range_1(RGB)
def hdr_IPT_to_XYZ(IPT_hdr, Y_s=0.2, Y_abs=100, method='Fairchild 2011'): """ Converts from *hdr-IPT* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- IPT_hdr : array_like *hdr-IPT* colourspace array. Y_s : numeric or array_like Relative luminance :math:`Y_s` of the surround in domain [0, 1]. 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 *CIE XYZ* tristimulus values. References ---------- - :cite:`Fairchild2010` - :cite:`Fairchild2011` Examples -------- >>> IPT_hdr = np.array([93.53174734, 1.85641567, -1.32922546]) >>> hdr_IPT_to_XYZ(IPT_hdr) # doctest: +ELLIPSIS array([ 0.9690723..., 1. , 1.1217921...]) >>> IPT_hdr = np.array([94.65929175, 0.38041773, -0.26731187]) >>> hdr_IPT_to_XYZ(IPT_hdr, method='Fairchild 2010') ... # doctest: +ELLIPSIS array([ 0.9690723..., 1. , 1.1217921...]) """ 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': luminance_callable = luminance_Fairchild2010 else: luminance_callable = luminance_Fairchild2011 e = exponent_hdr_IPT(Y_s, Y_abs, method)[..., np.newaxis] LMS = dot_vector(IPT_IPT_TO_LMS_MATRIX, IPT_hdr) LMS_prime = np.sign(LMS) * np.abs(luminance_callable(LMS, e)) XYZ = dot_vector(IPT_LMS_TO_XYZ_MATRIX, LMS_prime) return XYZ
def XYZ_to_IPT(XYZ): """ Converts from *CIE XYZ* tristimulus values to *IPT* colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray *IPT* colourspace array. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``IPT`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``P`` : [-1, 1] | ``P`` : [-1, 1] | | | | | | | ``T`` : [-1, 1] | ``T`` : [-1, 1] | +------------+-----------------------+-----------------+ - Input *CIE XYZ* tristimulus values needs to be adapted for *CIE Standard Illuminant D Series* *D65*. References ---------- :cite:`Fairchild2013y` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_IPT(XYZ) # doctest: +ELLIPSIS array([ 0.3842619..., 0.3848730..., 0.1888683...]) """ XYZ = to_domain_1(XYZ) LMS = dot_vector(IPT_XYZ_TO_LMS_MATRIX, XYZ) LMS_prime = spow(LMS, 0.43) IPT = dot_vector(IPT_LMS_TO_IPT_MATRIX, LMS_prime) return from_range_1(IPT)
def IPT_to_XYZ(IPT): """ Converts from *IPT* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- IPT : array_like *IPT* colourspace array. Returns ------- ndarray *CIE XYZ* tristimulus values. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``IPT`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``P`` : [-1, 1] | ``P`` : [-1, 1] | | | | | | | ``T`` : [-1, 1] | ``T`` : [-1, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`Fairchild2013y` Examples -------- >>> IPT = np.array([0.38426191, 0.38487306, 0.18886838]) >>> IPT_to_XYZ(IPT) # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) """ IPT = to_domain_1(IPT) LMS = dot_vector(IPT_IPT_TO_LMS_MATRIX, IPT) LMS_prime = spow(LMS, 1 / 0.43) XYZ = dot_vector(IPT_LMS_TO_XYZ_MATRIX, LMS_prime) return from_range_1(XYZ)
def plot_cvd_simulation_Machado2009(RGB, deficiency='Protanomaly', severity=0.5, M_a=None, **kwargs): """ Performs colour vision deficiency simulation on given *RGB* colourspace array using *Machado et al. (2009)* model. Parameters ---------- RGB : array_like *RGB* colourspace array. deficiency : unicode, optional {'Protanomaly', 'Deuteranomaly', 'Tritanomaly'} Colour blindness / vision deficiency type. severity : numeric, optional Severity of the colour vision deficiency in domain [0, 1]. M_a : array_like, optional Anomalous trichromacy matrix to use instead of Machado (2010) pre-computed matrix. Other Parameters ---------------- \\**kwargs : dict, optional {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_image`, :func:`colour.plotting.render`}, Please refer to the documentation of the previously listed definitions. Notes ----- - Input *RGB* array is expected to be linearly encoded. Returns ------- tuple Current figure and axes. Examples -------- >>> import numpy as np >>> RGB = np.random.rand(32, 32, 3) >>> plot_cvd_simulation_Machado2009(RGB) # doctest: +SKIP .. image:: ../_static/Plotting_Plot_CVD_Simulation_Machado2009.png :align: center :alt: plot_cvd_simulation_Machado2009 """ if M_a is None: M_a = cvd_matrix_Machado2009(deficiency, severity) text = 'Deficiency: {0} - Severity: {1}'.format(deficiency, severity) settings = {'text_parameters': {'text': None if M_a is None else text}} settings.update(kwargs) return plot_image( COLOUR_STYLE_CONSTANTS.colour.colourspace.encoding_cctf( dot_vector(M_a, RGB)), **settings)
def XYZ_to_RGB_LLAB(XYZ): """ Converts from *CIE XYZ* tristimulus values to normalised cone responses. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray Normalised cone responses. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_to_RGB_LLAB(XYZ) # doctest: +ELLIPSIS array([ 0.9414279..., 1.0404012..., 1.0897088...]) """ _X, Y, _Z = tsplit(XYZ) Y = tstack((Y, Y, Y)) XYZ_n = XYZ / Y return dot_vector(LLAB_XYZ_TO_RGB_MATRIX, XYZ_n)
def XYZ_to_RGB_LLAB(XYZ): """ Converts from *CIE XYZ* tristimulus values to normalised cone responses. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray Normalised cone responses. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_to_RGB_LLAB(XYZ) # doctest: +ELLIPSIS array([ 0.9414279..., 1.0404012..., 1.0897088...]) """ _X, Y, Z = tsplit(XYZ) Y = tstack((Y, Y, Y)) XYZ_n = XYZ / Y return dot_vector(LLAB_XYZ_TO_RGB_MATRIX, XYZ_n)
def XYZ_to_LMS_ATD95(XYZ): """ Converts from *CIE XYZ* tristimulus values to *LMS* cone responses. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray *LMS* cone responses. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_to_LMS_ATD95(XYZ) # doctest: +ELLIPSIS array([ 6.2283272..., 7.4780666..., 3.8859772...]) """ LMS = dot_vector([ [0.2435, 0.8524, -0.0516], [-0.3954, 1.1642, 0.0837], [0.0000, 0.0400, 0.6225], ], XYZ) LMS *= np.array([0.66, 1.0, 0.43]) LMS = spow(LMS, 0.7) LMS += np.array([0.024, 0.036, 0.31]) return LMS
def test_dot_vector(self): """ Tests :func:`colour.utilities.array.dot_vector` definition. """ m = np.array([ [0.7328, 0.4296, -0.1624], [-0.7036, 1.6975, 0.0061], [0.0030, 0.0136, 0.9834], ]) m = np.reshape(np.tile(m, (6, 1)), (6, 3, 3)) v = np.array([0.20654008, 0.12197225, 0.05136952]) v = np.tile(v, (6, 1)) np.testing.assert_almost_equal( dot_vector(m, v), np.array([ [0.19540944, 0.06203965, 0.05279523], [0.19540944, 0.06203965, 0.05279523], [0.19540944, 0.06203965, 0.05279523], [0.19540944, 0.06203965, 0.05279523], [0.19540944, 0.06203965, 0.05279523], [0.19540944, 0.06203965, 0.05279523], ]), decimal=7)
def RGB_to_YCoCg(RGB): """ Converts an array of *R'G'B'* values to the corresponding *YCoCg* colour encoding values array. Parameters ---------- RGB : array_like Input *R'G'B'* array. Returns ------- ndarray *YCoCg* colour encoding array. References ---------- :cite:`Malvar2003` Examples -------- >>> RGB_to_YCoCg(np.array([1.0, 1.0, 1.0])) array([ 1., 0., 0.]) >>> RGB_to_YCoCg(np.array([0.75, 0.5, 0.5])) array([ 0.5625, 0.125 , -0.0625]) """ return dot_vector(RGB_TO_YCOCG_MATRIX, RGB)
def YCoCg_to_RGB(YCoCg): """ Converts an array of *YCoCg* colour encoding values to the corresponding *R'G'B'* values array. Parameters ---------- YCoCg : array_like *YCoCg* colour encoding array. Returns ------- ndarray Output *R'G'B'* array. References ---------- :cite:`Malvar2003` Examples -------- >>> YCoCg_to_RGB(np.array([1.0, 0.0, 0.0])) array([ 1., 1., 1.]) >>> YCoCg_to_RGB(np.array([0.5625, 0.125, -0.0625])) array([ 0.75, 0.5 , 0.5 ]) """ return dot_vector(YCOCG_TO_RGB_MATRIX, YCoCg)
def RGB_to_rgb(RGB): """ Converts given *RGB* array to *Hunt-Pointer-Estevez* :math:`\\rho\gamma\\beta` colourspace. Parameters ---------- RGB : array_like *RGB* array. Returns ------- ndarray *Hunt-Pointer-Estevez* :math:`\\rho\gamma\\beta` colourspace array. Examples -------- >>> RGB = np.array([19.99370783, 20.00393634, 20.01326387]) >>> RGB_to_rgb(RGB) # doctest: +ELLIPSIS array([ 19.9969397..., 20.0018612..., 20.0135053...]) """ rgb = dot_vector(dot_matrix(XYZ_TO_HPE_MATRIX, CAT02_INVERSE_CAT), RGB) return rgb
def rgb_to_RGB(rgb): """ Converts given *Hunt-Pointer-Estevez* :math:`\\rho\gamma\\beta` colourspace array to *RGB* array. Parameters ---------- rgb : array_like *Hunt-Pointer-Estevez* :math:`\\rho\gamma\\beta` colourspace array. Returns ------- ndarray *RGB* array. Examples -------- >>> rgb = np.array([19.99693975, 20.00186123, 20.01350530]) >>> rgb_to_RGB(rgb) # doctest: +ELLIPSIS array([ 19.9937078..., 20.0039363..., 20.0132638...]) """ RGB = dot_vector(dot_matrix(CAT02_CAT, HPE_TO_XYZ_MATRIX), rgb) return RGB
def test_dot_vector(self): """ Tests :func:`colour.utilities.array.dot_vector` definition. """ m = np.array([ [0.7328, 0.4296, -0.1624], [-0.7036, 1.6975, 0.0061], [0.0030, 0.0136, 0.9834], ]) m = np.reshape(np.tile(m, (6, 1)), (6, 3, 3)) v = np.array([0.07049534, 0.10080000, 0.09558313]) v = np.tile(v, (6, 1)) np.testing.assert_almost_equal( dot_vector(m, v), np.array([ [0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882], ]), decimal=7)
def highlights_recovery_blend(RGB, multipliers, threshold=0.99): """ Performs highlights recovery using *Coffin (1997)* method from *dcraw*. Parameters ---------- RGB : array_like *RGB* colourspace array. multipliers : array_like Normalised camera white level or white balance multipliers. threshold : numeric, optional Threshold for highlights selection. Returns ------- ndarray Highlights recovered *RGB* colourspace array. References ---------- :cite:`Coffin2015a` """ M = np.array( [[1.0000000, 1.0000000, 1.0000000], [1.7320508, -1.7320508, 0.0000000], [-1.0000000, -1.0000000, 2.0000000]]) # yapf: disable clipping_level = np.min(multipliers) * threshold Lab = dot_vector(M, RGB) Lab_c = dot_vector(M, np.minimum(RGB, clipping_level)) s = np.sum((Lab * Lab)[..., 1:3], axis=2) s_c = np.sum((Lab_c * Lab_c)[..., 1:3], axis=2) ratio = np.sqrt(s_c / s) ratio[np.logical_or(np.isnan(ratio), np.isinf(ratio))] = 1 Lab[:, :, 1:3] *= np.rollaxis(ratio[np.newaxis], 0, 3) RGB_o = dot_vector(np.linalg.inv(M), Lab) return RGB_o
def degrees_of_adaptation(LMS, Y_n, v=1 / 3, discount_illuminant=False): """ Computes the degrees of adaptation :math:`p_L`, :math:`p_M` and :math:`p_S`. Parameters ---------- LMS : array_like Cone responses. Y_n : numeric or array_like Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. v : numeric or array_like, optional Exponent :math:`v`. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Returns ------- ndarray Degrees of adaptation :math:`p_L`, :math:`p_M` and :math:`p_S`. Examples -------- >>> LMS = np.array([20.00052060, 19.99978300, 19.99883160]) >>> Y_n = 31.83 >>> degrees_of_adaptation(LMS, Y_n) # doctest: +ELLIPSIS array([ 0.9799324..., 0.9960035..., 1.0233041...]) >>> degrees_of_adaptation(LMS, Y_n, 1 / 3, True) array([ 1., 1., 1.]) """ LMS = np.asarray(LMS) if discount_illuminant: return np.ones(LMS.shape) Y_n = np.asarray(Y_n) v = np.asarray(v) L, M, S = tsplit(LMS) LMS_E = dot_vector(VON_KRIES_CAT, np.ones(LMS.shape)) # E illuminant. L_E, M_E, S_E = tsplit(LMS_E) Ye_n = Y_n ** v f_E = lambda x, y: (3 * (x / y)) / (L / L_E + M / M_E + S / S_E) f_P = lambda x: (1 + Ye_n + x) / (1 + Ye_n + 1 / x) p_L = f_P(f_E(L, L_E)) p_M = f_P(f_E(M, M_E)) p_S = f_P(f_E(S, S_E)) p_LMS = tstack((p_L, p_M, p_S)) return p_LMS
def RGB_to_RGB(RGB, input_colourspace, output_colourspace, chromatic_adaptation_transform='CAT02'): """ Converts from given input *RGB* colourspace to output *RGB* colourspace using given *chromatic adaptation* method. Parameters ---------- RGB : array_like *RGB* colourspace array. input_colourspace : RGB_Colourspace *RGB* input colourspace. output_colourspace : RGB_Colourspace *RGB* output colourspace. chromatic_adaptation_transform : unicode, optional **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco', 'Bianco PC'}**, *Chromatic adaptation* transform. Returns ------- ndarray *RGB* colourspace array. Notes ----- - Input / output *RGB* colourspace arrays are in domain / range [0, 1]. - Input / output *RGB* colourspace arrays are assumed to be representing linear light values. Examples -------- >>> from colour import sRGB_COLOURSPACE, PROPHOTO_RGB_COLOURSPACE >>> RGB = np.array([0.01103742, 0.12734226, 0.11632971]) >>> RGB_to_RGB( ... RGB, ... sRGB_COLOURSPACE, ... PROPHOTO_RGB_COLOURSPACE) # doctest: +ELLIPSIS array([ 0.0643538..., 0.1157289..., 0.1158038...]) """ cat = chromatic_adaptation_matrix_VonKries( xy_to_XYZ(input_colourspace.whitepoint), xy_to_XYZ(output_colourspace.whitepoint), chromatic_adaptation_transform) M = dot_matrix(cat, input_colourspace.RGB_to_XYZ_matrix) M = dot_matrix(output_colourspace.XYZ_to_RGB_matrix, M) RGB = dot_vector(M, RGB) return RGB
def degrees_of_adaptation(LMS, Y_n, v=1 / 3, discount_illuminant=False): """ Computes the degrees of adaptation :math:`p_L`, :math:`p_M` and :math:`p_S`. Parameters ---------- LMS : array_like Cone responses. Y_n : numeric or array_like Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`. v : numeric or array_like, optional Exponent :math:`v`. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Returns ------- ndarray Degrees of adaptation :math:`p_L`, :math:`p_M` and :math:`p_S`. Examples -------- >>> LMS = np.array([20.00052060, 19.99978300, 19.99883160]) >>> Y_n = 31.83 >>> degrees_of_adaptation(LMS, Y_n) # doctest: +ELLIPSIS array([ 0.9799324..., 0.9960035..., 1.0233041...]) >>> degrees_of_adaptation(LMS, Y_n, 1 / 3, True) array([ 1., 1., 1.]) """ LMS = np.asarray(LMS) if discount_illuminant: return np.ones(LMS.shape) Y_n = np.asarray(Y_n) v = np.asarray(v) L, M, S = tsplit(LMS) LMS_E = dot_vector(VON_KRIES_CAT, np.ones(LMS.shape)) # E illuminant. L_E, M_E, S_E = tsplit(LMS_E) Ye_n = Y_n**v f_E = lambda x, y: (3 * (x / y)) / (L / L_E + M / M_E + S / S_E) f_P = lambda x: (1 + Ye_n + x) / (1 + Ye_n + 1 / x) p_L = f_P(f_E(L, L_E)) p_M = f_P(f_E(M, M_E)) p_S = f_P(f_E(S, S_E)) p_LMS = tstack((p_L, p_M, p_S)) return p_LMS
def RGB_to_RGB(RGB, input_colourspace, output_colourspace, chromatic_adaptation_transform='CAT02'): """ Converts from given input *RGB* colourspace to output *RGB* colourspace using given *chromatic adaptation* method. Parameters ---------- RGB : array_like *RGB* colourspace array. input_colourspace : RGB_Colourspace *RGB* input colourspace. output_colourspace : RGB_Colourspace *RGB* output colourspace. chromatic_adaptation_transform : unicode, optional **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', 'Fairchild, 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco', 'Bianco PC'}**, *Chromatic adaptation* transform. Returns ------- ndarray *RGB* colourspace array. Notes ----- - *RGB* colourspace arrays are in domain [0, 1]. Examples -------- >>> from colour import sRGB_COLOURSPACE, PROPHOTO_RGB_COLOURSPACE >>> RGB = np.array([0.01103604, 0.12734466, 0.11631037]) >>> RGB_to_RGB( ... RGB, ... sRGB_COLOURSPACE, ... PROPHOTO_RGB_COLOURSPACE) # doctest: +ELLIPSIS array([ 0.0643338..., 0.1157362..., 0.1157614...]) """ cat = chromatic_adaptation_matrix_VonKries( xy_to_XYZ(input_colourspace.whitepoint), xy_to_XYZ(output_colourspace.whitepoint), chromatic_adaptation_transform) M = dot_matrix(cat, input_colourspace.RGB_to_XYZ_matrix) M = dot_matrix(output_colourspace.XYZ_to_RGB_matrix, M) RGB = dot_vector(M, RGB) return RGB
def chromatic_adaptation(RGB, RGB_0, RGB_0r, Y, D=1): """ Applies chromatic adaptation to given *RGB* normalised cone responses array. Parameters ---------- RGB : array_like *RGB* normalised cone responses array of test sample / stimulus. RGB_0 : array_like *RGB* normalised cone responses array of reference white. RGB_0r : array_like *RGB* normalised cone responses array of reference illuminant *CIE Standard Illuminant D Series* *D65*. Y : numeric or array_like Tristimulus values :math:`Y` of the stimulus. D : numeric or array_like, optional *Discounting-the-Illuminant* factor in domain [0, 1]. Returns ------- ndarray Adapted *CIE XYZ* tristimulus values. Examples -------- >>> RGB = np.array([0.94142795, 1.04040120, 1.08970885]) >>> RGB_0 = np.array([0.94146023, 1.04039386, 1.08950293]) >>> RGB_0r = np.array([0.94146023, 1.04039386, 1.08950293]) >>> Y = 20.0 >>> chromatic_adaptation(RGB, RGB_0, RGB_0r, Y) # doctest: +ELLIPSIS array([ 19.01, 20. , 21.78]) """ R, G, B = tsplit(RGB) R_0, G_0, B_0 = tsplit(RGB_0) R_0r, G_0r, B_0r = tsplit(RGB_0r) Y = np.asarray(Y) beta = (B_0 / B_0r) ** 0.0834 R_r = (D * (R_0r / R_0) + 1 - D) * R G_r = (D * (G_0r / G_0) + 1 - D) * G B_r = (D * (B_0r / (B_0 ** beta)) + 1 - D) * (abs(B) ** beta) RGB_r = tstack((R_r, G_r, B_r)) Y = tstack((Y, Y, Y)) XYZ_r = dot_vector(LLAB_RGB_TO_XYZ_MATRIX, RGB_r * Y) return XYZ_r
def chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr, transform='CAT02'): """ 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 whitepoint. XYZ_wr : array_like Reference viewing condition *CIE XYZ* tristimulus values of whitepoint. transform : unicode, optional **{'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. References ---------- - :cite:`Fairchild2013t` Examples -------- >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313]) >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280]) >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037]) >>> chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr) # doctest: +ELLIPSIS array([ 0.0839746..., 0.1141321..., 0.2862554...]) Using Bradford method: >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313]) >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280]) >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037]) >>> transform = 'Bradford' >>> chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr, transform) ... # doctest: +ELLIPSIS array([ 0.0854032..., 0.1140122..., 0.2972149...]) """ cat = chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform) XYZ_a = dot_vector(cat, XYZ) return XYZ_a
def XYZ_to_hdr_IPT(XYZ, Y_s=0.2, Y_abs=100): """ 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 in domain [0, 1]. Y_abs : numeric or array_like Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. Returns ------- ndarray *hdr-IPT* colourspace array. Notes ----- - Input *CIE XYZ* tristimulus values needs to be adapted for *CIE Standard Illuminant D Series* *D65*. Examples -------- >>> XYZ = np.array([0.96907232, 1.00000000, 1.12179215]) >>> XYZ_to_hdr_IPT(XYZ) # doctest: +ELLIPSIS array([ 94.6592917..., 0.3804177..., -0.2673118...]) """ e = exponent_hdr_IPT(Y_s, Y_abs)[..., np.newaxis] LMS = dot_vector(IPT_XYZ_TO_LMS_MATRIX, XYZ) LMS_prime = np.sign(LMS) * np.abs(lightness_Fairchild2010(LMS, e)) IPT = dot_vector(IPT_LMS_TO_IPT_MATRIX, LMS_prime) return IPT
def RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wavelength): """ Converts *Stiles & Burch 1959 10 Degree RGB CMFs* colour matching functions into the *CIE 1964 10 Degree Standard Observer* colour matching functions. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\lambda` in nm. Returns ------- ndarray *CIE 1964 10 Degree Standard Observer* spectral tristimulus values. See Also -------- :attr:`colour.colorimetry.dataset.cmfs.RGB_CMFS` Notes ----- - Data for the *CIE 1964 10 Degree Standard Observer* already exists, this definition is intended for educational purpose. References ---------- .. [2] Wyszecki, G., & Stiles, W. S. (2000). The CIE 1964 Standard Observer. In Color Science: Concepts and Methods, Quantitative Data and Formulae (p. 141). Wiley. ISBN:978-0471399186 Examples -------- >>> RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(700) # doctest: +ELLIPSIS array([ 9.6432150...e-03, 3.7526317...e-03, -4.1078830...e-06]) """ cmfs = RGB_CMFS['Stiles & Burch 1959 10 Degree RGB CMFs'] rgb_bar = cmfs.get(wavelength) M = np.array( [[0.341080, 0.189145, 0.387529], [0.139058, 0.837460, 0.073316], [0.000000, 0.039553, 2.026200]]) # yapf: disable xyz_bar = dot_vector(M, rgb_bar) return xyz_bar
def RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wavelength): """ Converts *Stiles & Burch 1959 10 Degree RGB CMFs* colour matching functions into the *Stockman & Sharpe 10 Degree Cone Fundamentals* spectral sensitivity functions. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\lambda` in nm. Returns ------- ndarray *Stockman & Sharpe 10 Degree Cone Fundamentals* spectral tristimulus values. Notes ----- - Data for the *Stockman & Sharpe 10 Degree Cone Fundamentals* already exists, this definition is intended for educational purpose. References ---------- - :cite:`CIETC1-362006a` Examples -------- >>> from colour.utilities import numpy_print_options >>> with numpy_print_options(suppress=True): ... RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(700) # doctest: +ELLIPSIS array([ 0.0052860..., 0.0003252..., 0. ]) """ cmfs = RGB_CMFS['Stiles & Burch 1959 10 Degree RGB CMFs'] rgb_bar = cmfs[wavelength] M = np.array([ [0.1923252690, 0.749548882, 0.0675726702], [0.0192290085, 0.940908496, 0.113830196], [0.0000000000, 0.0105107859, 0.991427669], ]) lms_bar = dot_vector(M, rgb_bar) lms_bar[..., -1][np.asarray(np.asarray(wavelength) > 505)] = 0 return lms_bar
def RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(wavelength): """ Converts *Stiles & Burch 1959 10 Degree RGB CMFs* colour matching functions into the *CIE 1964 10 Degree Standard Observer* colour matching functions. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\lambda` in nm. Returns ------- ndarray *CIE 1964 10 Degree Standard Observer* spectral tristimulus values. See Also -------- :attr:`colour.colorimetry.dataset.cmfs.RGB_CMFS` Notes ----- - Data for the *CIE 1964 10 Degree Standard Observer* already exists, this definition is intended for educational purpose. References ---------- .. [2] Wyszecki, G., & Stiles, W. S. (2000). The CIE 1964 Standard Observer. In Color Science: Concepts and Methods, Quantitative Data and Formulae (p. 141). Wiley. ISBN:978-0471399186 Examples -------- >>> RGB_10_degree_cmfs_to_XYZ_10_degree_cmfs(700) # doctest: +ELLIPSIS array([ 9.6432150...e-03, 3.7526317...e-03, -4.1078830...e-06]) """ cmfs = RGB_CMFS.get('Stiles & Burch 1959 10 Degree RGB CMFs') rgb_bar = cmfs.get(wavelength) M = np.array([[0.341080, 0.189145, 0.387529], [0.139058, 0.837460, 0.073316], [0.000000, 0.039553, 2.026200]]) xyz_bar = dot_vector(M, rgb_bar) return xyz_bar
def RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wavelength): """ Converts *Stiles & Burch 1959 10 Degree RGB CMFs* colour matching functions into the *Stockman & Sharpe 10 Degree Cone Fundamentals* spectral sensitivity functions. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\lambda` in nm. Returns ------- ndarray *Stockman & Sharpe 10 Degree Cone Fundamentals* spectral tristimulus values. Notes ----- - Data for the *Stockman & Sharpe 10 Degree Cone Fundamentals* already exists, this definition is intended for educational purpose. References ---------- .. [3] CIE TC 1-36. (2006). CIE 170-1:2006 Fundamental Chromaticity Diagram with Physiological Axes - Part 1 (pp. 1–56). ISBN:978-3-901-90646-6 Examples -------- >>> RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(700) # doctest: +ELLIPSIS array([ 0.0052860..., 0.0003252..., 0. ]) """ cmfs = RGB_CMFS['Stiles & Burch 1959 10 Degree RGB CMFs'] rgb_bar = cmfs.get(wavelength) M = np.array( [[0.1923252690, 0.749548882, 0.0675726702], [0.0192290085, 0.940908496, 0.113830196], [0.0000000000, 0.0105107859, 0.991427669]]) # yapf: disable lms_bar = dot_vector(M, rgb_bar) lms_bar[..., -1][np.asarray(np.asarray(wavelength) > 505)] = 0 return lms_bar
def chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr, transform='CAT02'): """ 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 whitepoint. XYZ_wr : array_like Reference viewing condition *CIE XYZ* tristimulus values of whitepoint. transform : unicode, optional **{'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. Examples -------- >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313]) >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280]) >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037]) >>> chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr) # doctest: +ELLIPSIS array([ 0.0839746..., 0.1141321..., 0.2862554...]) Using Bradford method: >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313]) >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280]) >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037]) >>> method = 'Bradford' >>> chromatic_adaptation_VonKries( # doctest: +ELLIPSIS ... XYZ, XYZ_w, XYZ_wr, method) array([ 0.0854032..., 0.1140122..., 0.2972149...]) """ cat = chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform) XYZ_a = dot_vector(cat, XYZ) return XYZ_a
def LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wavelength): """ Converts *Stockman & Sharpe 10 Degree Cone Fundamentals* colour matching functions into the *CIE 2012 10 Degree Standard Observer* colour matching functions. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\lambda` in nm. Returns ------- ndarray *CIE 2012 10 Degree Standard Observer* spectral tristimulus values. Notes ----- - Data for the *CIE 2012 10 Degree Standard Observer* already exists, this definition is intended for educational purpose. References ---------- - :cite:`CVRLp` Examples -------- >>> from colour.utilities import numpy_print_options >>> with numpy_print_options(suppress=True): ... LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(700) # doctest: +ELLIPSIS array([ 0.0098162..., 0.0037761..., 0. ]) """ cmfs = LMS_CMFS['Stockman & Sharpe 10 Degree Cone Fundamentals'] lms_bar = cmfs[wavelength] M = np.array([ [1.93986443, -1.34664359, 0.43044935], [0.69283932, 0.34967567, 0.00000000], [0.00000000, 0.00000000, 2.14687945], ]) xyz_bar = dot_vector(M, lms_bar) return xyz_bar
def RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(wavelength): """ Converts *Stiles & Burch 1959 10 Degree RGB CMFs* colour matching functions into the *Stockman & Sharpe 10 Degree Cone Fundamentals* spectral sensitivity functions. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\lambda` in nm. Returns ------- ndarray *Stockman & Sharpe 10 Degree Cone Fundamentals* spectral tristimulus values. Notes ----- - Data for the *Stockman & Sharpe 10 Degree Cone Fundamentals* already exists, this definition is intended for educational purpose. References ---------- .. [3] CIE TC 1-36. (2006). CIE 170-1:2006 Fundamental Chromaticity Diagram with Physiological Axes - Part 1 (pp. 1–56). ISBN:978-3-901-90646-6 Examples -------- >>> RGB_10_degree_cmfs_to_LMS_10_degree_cmfs(700) # doctest: +ELLIPSIS array([ 0.0052860..., 0.0003252..., 0. ]) """ cmfs = RGB_CMFS.get('Stiles & Burch 1959 10 Degree RGB CMFs') rgb_bar = cmfs.get(wavelength) M = np.array([[0.1923252690, 0.749548882, 0.0675726702], [0.0192290085, 0.940908496, 0.113830196], [0.0000000000, 0.0105107859, 0.991427669]]) lms_bar = dot_vector(M, rgb_bar) lms_bar[..., -1][np.asarray(np.asarray(wavelength) > 505)] = 0 return lms_bar
def LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(wavelength): """ Converts *Stockman & Sharpe 10 Degree Cone Fundamentals* colour matching functions into the *CIE 2012 10 Degree Standard Observer* colour matching functions. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\lambda` in nm. Returns ------- ndarray *CIE 2012 10 Degree Standard Observer* spectral tristimulus values. Notes ----- - Data for the *CIE 2012 10 Degree Standard Observer* already exists, this definition is intended for educational purpose. References ---------- .. [5] CVRL. (n.d.). CIE (2012) 10-deg XYZ “physiologically-relevant” colour matching functions. Retrieved June 25, 2014, from http://www.cvrl.org/database/text/cienewxyz/cie2012xyz10.htm Examples -------- >>> LMS_10_degree_cmfs_to_XYZ_10_degree_cmfs(700) # doctest: +ELLIPSIS array([ 0.0098162..., 0.0037761..., 0. ]) """ cmfs = LMS_CMFS.get('Stockman & Sharpe 10 Degree Cone Fundamentals') lms_bar = cmfs.get(wavelength) M = np.array([[1.93986443, -1.34664359, 0.43044935], [0.69283932, 0.34967567, 0.00000000], [0.00000000, 0.00000000, 2.14687945]]) xyz_bar = dot_vector(M, lms_bar) return xyz_bar
def LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(wavelength): """ Converts *Stockman & Sharpe 2 Degree Cone Fundamentals* colour matching functions into the *CIE 2012 2 Degree Standard Observer* colour matching functions. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\lambda` in nm. Returns ------- ndarray *CIE 2012 2 Degree Standard Observer* spectral tristimulus values. Notes ----- - Data for the *CIE 2012 2 Degree Standard Observer* already exists, this definition is intended for educational purpose. References ---------- .. [4] CVRL. (n.d.). CIE (2012) 2-deg XYZ “physiologically-relevant” colour matching functions. Retrieved June 25, 2014, from http://www.cvrl.org/database/text/cienewxyz/cie2012xyz2.htm Examples -------- >>> LMS_2_degree_cmfs_to_XYZ_2_degree_cmfs(700) # doctest: +ELLIPSIS array([ 0.0109677..., 0.0041959..., 0. ]) """ cmfs = LMS_CMFS.get('Stockman & Sharpe 2 Degree Cone Fundamentals') lms_bar = cmfs.get(wavelength) M = np.array([[1.94735469, -1.41445123, 0.36476327], [0.68990272, 0.34832189, 0.00000000], [0.00000000, 0.00000000, 1.93485343]]) xyz_bar = dot_vector(M, lms_bar) return xyz_bar
def XYZ_to_RGB_cie1994(XYZ): """ Converts from *CIE XYZ* tristimulus values to cone responses. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray Cone responses. Examples -------- >>> XYZ = np.array([28.00, 21.26, 5.27]) >>> XYZ_to_RGB_cie1994(XYZ) # doctest: +ELLIPSIS array([ 25.8244273..., 18.6791422..., 4.8390194...]) """ return dot_vector(CIE1994_XYZ_TO_RGB_MATRIX, XYZ)
def RGB_to_XYZ_cie1994(RGB): """ Converts from cone responses to *CIE XYZ* tristimulus values. Parameters ---------- RGB : array_like Cone responses. Returns ------- ndarray *CIE XYZ* tristimulus values. Examples -------- >>> RGB = np.array([25.82442730, 18.67914220, 4.83901940]) >>> RGB_to_XYZ_cie1994(RGB) # doctest: +ELLIPSIS array([ 28. , 21.26, 5.27]) """ return dot_vector(CIE1994_RGB_TO_XYZ_MATRIX, RGB)
def XYZ_to_RGB_Nayatani95(XYZ): """ Converts from *CIE XYZ* tristimulus values to cone responses. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray Cone responses. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_to_RGB_Nayatani95(XYZ) # doctest: +ELLIPSIS array([ 20.000520..., 19.999783..., 19.998831...]) """ return dot_vector(NAYATANI95_XYZ_TO_RGB_MATRIX, XYZ)
def RGB_to_XYZ_Fairchild1990(RGB): """ Converts from cone responses to *CIE XYZ* tristimulus values. Parameters ---------- RGB : array_like Cone responses. Returns ------- ndarray *CIE XYZ* tristimulus values. Examples -------- >>> RGB = np.array([22.12319350, 23.60542240, 22.92795340]) >>> RGB_to_XYZ_Fairchild1990(RGB) # doctest: +ELLIPSIS array([ 19.53, 23.07, 24.97]) """ return dot_vector(FAIRCHILD1990_RGB_TO_XYZ_MATRIX, RGB)
def XYZ_to_RGB_Fairchild1990(XYZ): """ Converts from *CIE XYZ* tristimulus values to cone responses. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray Cone responses. Examples -------- >>> XYZ = np.array([19.53, 23.07, 24.97]) >>> XYZ_to_RGB_Fairchild1990(XYZ) # doctest: +ELLIPSIS array([ 22.1231935..., 23.6054224..., 22.9279534...]) """ return dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ)
def test_dot_vector(self): """ Tests :func:`colour.utilities.array.dot_vector` definition. """ m = np.array([[0.7328, 0.4296, -0.1624], [-0.7036, 1.6975, 0.0061], [0.0030, 0.0136, 0.9834]]) m = np.reshape(np.tile(m, (6, 1)), (6, 3, 3)) v = np.array([0.07049534, 0.10080000, 0.09558313]) v = np.tile(v, (6, 1)) np.testing.assert_almost_equal( dot_vector(m, v), np.array([[0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882], [0.07943996, 0.12209054, 0.09557882]]), decimal=7)
def XYZ_to_rgb(XYZ): """ Converts from *CIE XYZ* tristimulus values to *Hunt-Pointer-Estevez* :math:`\\rho\gamma\\beta` colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray *Hunt-Pointer-Estevez* :math:`\\rho\gamma\\beta` colourspace. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_to_rgb(XYZ) # doctest: +ELLIPSIS array([ 19.4743367..., 20.3101217..., 21.78 ]) """ return dot_vector(XYZ_TO_HPE_MATRIX, XYZ)
def XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround=CIECAM02_VIEWING_CONDITIONS.get('Average'), discount_illuminant=False): """ Computes the CIECAM02 colour appearance model correlates from given *CIE XYZ* tristimulus values. This is the *forward* implementation. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of test sample / stimulus in domain [0, 100]. XYZ_w : array_like *CIE XYZ* tristimulus values of reference white in domain [0, 100]. L_A : numeric or array_like Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. Y_b : numeric or array_like Adapting field *Y* tristimulus value :math:`Y_b`. surround : CIECAM02_InductionFactors, optional Surround viewing conditions induction factors. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Returns ------- CIECAM02_Specification CIECAM02 colour appearance model specification. Warning ------- The input domain of that definition is non standard! Notes ----- - Input *CIE XYZ* tristimulus values are in domain [0, 100]. - Input *CIE XYZ_w* tristimulus values are in domain [0, 100]. Examples -------- >>> 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'] >>> XYZ_to_CIECAM02(XYZ, XYZ_w, L_A, Y_b, surround) # doctest: +ELLIPSIS CIECAM02_Specification(J=41.7310911..., C=0.1047077..., h=219.0484326..., \ s=2.3603053..., Q=195.3713259..., M=0.1088421..., H=array(278.0607358...), \ HC=None) """ _X_w, Y_w, _Z_w = tsplit(XYZ_w) L_A = np.asarray(L_A) Y_b = np.asarray(Y_b) n, F_L, N_bb, N_cb, z = tsplit(viewing_condition_dependent_parameters( Y_b, Y_w, L_A)) # Converting *CIE XYZ* tristimulus values to CMCCAT2000 transform # sharpened *RGB* values. RGB = dot_vector(CAT02_CAT, XYZ) 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 1 # Computing full chromatic adaptation. RGB_c = full_chromatic_adaptation_forward( RGB, RGB_w, Y_w, D) RGB_wc = full_chromatic_adaptation_forward( RGB_w, RGB_w, Y_w, D) # Converting to *Hunt-Pointer-Estevez* colourspace. RGB_p = RGB_to_rgb(RGB_c) RGB_pw = RGB_to_rgb(RGB_wc) # Applying forward post-adaptation non linear response compression. RGB_a = post_adaptation_non_linear_response_compression_forward( RGB_p, F_L) RGB_aw = post_adaptation_non_linear_response_compression_forward( RGB_pw, F_L) # Converting to preliminary cartesian coordinates. a, b = tsplit(opponent_colour_dimensions_forward(RGB_a)) # ------------------------------------------------------------------------- # Computing the *hue* angle :math:`h`. h = hue_angle(a, b) # ------------------------------------------------------------------------- # Computing hue :math:`h` quadrature :math:`H`. H = hue_quadrature(h) # TODO: Compute hue composition. # Computing eccentricity factor *e_t*. e_t = eccentricity_factor(h) # Computing achromatic responses for the stimulus and the whitepoint. A = achromatic_response_forward(RGB_a, N_bb) A_w = achromatic_response_forward(RGB_aw, N_bb) # ------------------------------------------------------------------------- # Computing the correlate of *Lightness* :math:`J`. # ------------------------------------------------------------------------- J = lightness_correlate(A, A_w, surround.c, z) # ------------------------------------------------------------------------- # Computing the correlate of *brightness* :math:`Q`. # ------------------------------------------------------------------------- Q = brightness_correlate(surround.c, J, A_w, F_L) # ------------------------------------------------------------------------- # Computing the correlate of *chroma* :math:`C`. # ------------------------------------------------------------------------- C = chroma_correlate(J, n, surround.N_c, N_cb, e_t, a, b, RGB_a) # ------------------------------------------------------------------------- # Computing the correlate of *colourfulness* :math:`M`. # ------------------------------------------------------------------------- M = colourfulness_correlate(C, F_L) # ------------------------------------------------------------------------- # Computing the correlate of *saturation* :math:`s`. # ------------------------------------------------------------------------- s = saturation_correlate(M, Q) return CIECAM02_Specification(J, C, h, s, Q, M, H, None)
def CIECAM02_to_XYZ(J, C, h, XYZ_w, L_A, Y_b, surround=CIECAM02_VIEWING_CONDITIONS.get( 'Average'), discount_illuminant=False): """ Converts CIECAM02 specification to *CIE XYZ* tristimulus values. This is the *reverse* implementation. Parameters ---------- J : numeric or array_like Correlate of *Lightness* :math:`J`. C : numeric or array_like Correlate of *chroma* :math:`C`. h : numeric or array_like *Hue* angle :math:`h` in degrees. 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`. Y_b : numeric or array_like Adapting field *Y* tristimulus value :math:`Y_b`. surround : CIECAM02_Surround, optional Surround viewing conditions. discount_illuminant : bool, optional Discount the illuminant. Returns ------- XYZ : ndarray *CIE XYZ* tristimulus values. Warning ------- The output range of that definition is non standard! Notes ----- - Input *CIE XYZ_w* tristimulus values are in domain [0, 100]. - Output *CIE XYZ* tristimulus values are in range [0, 100]. Examples -------- >>> J = 41.731091132513917 >>> C = 0.104707757171105 >>> h = 219.04843265827190 >>> XYZ_w = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> Y_b = 20.0 >>> CIECAM02_to_XYZ(J, C, h, XYZ_w, L_A, Y_b) # doctest: +ELLIPSIS array([ 19.01..., 20... , 21.78...]) """ _X_w, Y_w, _Zw = tsplit(XYZ_w) n, F_L, N_bb, N_cb, z = tsplit(viewing_condition_dependent_parameters( Y_b, Y_w, L_A)) # 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 1 # Computation 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 responses for the stimulus and 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 XYZ
def chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr, transform='CAT02'): """ 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 whitepoint. XYZ_wr : array_like Reference viewing condition *CIE XYZ* tristimulus values of whitepoint. transform : unicode, optional **{'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_n`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_r`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_c`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Fairchild2013t` Examples -------- >>> 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_VonKries(XYZ, XYZ_w, XYZ_wr) # doctest: +ELLIPSIS array([ 0.2163881..., 0.1257 , 0.0384749...]) Using Bradford method: >>> 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]) >>> transform = 'Bradford' >>> chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr, transform) ... # doctest: +ELLIPSIS array([ 0.2166600..., 0.1260477..., 0.0385506...]) """ XYZ = to_domain_1(XYZ) M_CAT = chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform) XYZ_a = dot_vector(M_CAT, XYZ) return from_range_1(XYZ_a)