def test_nan_eotf_ST2084(self): """ Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition nan support. """ eotf_ST2084(np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]))
def test_n_dimensional_eotf_ST2084(self): """ Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition n-dimensional arrays support. """ N = 0.18 C = 1.738580491084806 np.testing.assert_almost_equal( eotf_ST2084(N), C, decimal=7) N = np.tile(N, 6) C = np.tile(C, 6) np.testing.assert_almost_equal( eotf_ST2084(N), C, decimal=7) N = np.reshape(N, (2, 3)) C = np.reshape(C, (2, 3)) np.testing.assert_almost_equal( eotf_ST2084(N), C, decimal=7) N = np.reshape(N, (2, 3, 1)) C = np.reshape(C, (2, 3, 1)) np.testing.assert_almost_equal( eotf_ST2084(N), C, decimal=7)
def test_eotf_ST2084(self): """ Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition. """ self.assertAlmostEqual( eotf_ST2084(0.0), 0.0, places=7) self.assertAlmostEqual( eotf_ST2084(0.079420969944927), 0.18, places=7) self.assertAlmostEqual( eotf_ST2084(0.149945732100180), 1.0, places=7) self.assertAlmostEqual( eotf_ST2084(1.0, 5000), 5000.0, places=7)
def test_nan_eotf_ST2084(self): """ Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition nan support. """ eotf_ST2084( np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]))
def test_eotf_ST2084(self): """ Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition. """ self.assertAlmostEqual(eotf_ST2084(0.0), 0.0, places=7) self.assertAlmostEqual(eotf_ST2084(0.508078421517399), 100, places=7) self.assertAlmostEqual(eotf_ST2084(0.652578597563067), 400, places=7) self.assertAlmostEqual(eotf_ST2084(1.0, 5000), 5000.0, places=7)
def test_domain_range_scale_eotf_ST2084(self): """ Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_ST2084` definition domain and range scale support. """ N = 0.508078421517399 C = eotf_ST2084(N) d_r = (('reference', 1), (1, 1), (100, 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( eotf_ST2084(N * factor), C * factor, decimal=7)
def eotf_BT2100_PQ(E_p): """ Defines *Recommendation ITU-R BT.2100* *Reference PQ* electro-optical transfer function (EOTF / EOCF). The EOTF maps the non-linear *PQ* signal into display light. Parameters ---------- E_p : numeric or array_like :math:`E'` denotes a non-linear colour value :math:`{R', G', B'}` or :math:`{L', M', S'}` in *PQ* space [0, 1]. Returns ------- numeric or ndarray :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. References ---------- - :cite:`Borer2017a` - :cite:`InternationalTelecommunicationUnion2016a` Examples -------- >>> eotf_BT2100_PQ(0.724769816665726) # doctest: +ELLIPSIS 779.9883608... """ return eotf_ST2084(E_p, 10000)
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 oetf_reverse_BT2100_PQ(E_p): """ Defines *Recommendation ITU-R BT.2100* *Reference PQ* reverse opto-electrical transfer function (OETF / OECF). Parameters ---------- E_p : numeric or array_like :math:`E` is the resulting non-linear signal (:math:`R'`, :math:`G'`, :math:`B'`) in the range [0, 1]. Returns ------- numeric or ndarray :math:`E = {R_S, G_S, B_S; Y_S; or I_S}` is the signal determined by scene light and scaled by camera exposure. The values :math:`E`, :math:`R_S`, :math:`G_S`, :math:`B_S`, :math:`Y_S`, :math:`I_S` are in the range [0, 1]. References ---------- - :cite:`Borer2017a` - :cite:`InternationalTelecommunicationUnion2016a` Examples -------- >>> oetf_reverse_BT2100_PQ(0.724769816665726) # doctest: +ELLIPSIS 0.0999999... """ return ootf_reverse_BT2100_PQ(eotf_ST2084(E_p, 10000))
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 ICaCb_to_XYZ(ICaCb: ArrayLike) -> NDArray: """ Convert from :math:`IC_AC_B` tristimulus values to *CIE XYZ* colourspace. Parameters ---------- ICaCb :math:`IC_AC_B` tristimulus values. Returns ------- :class:`numpy.ndarray` *CIE XYZ* colourspace array. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``ICaCb`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``Ca`` : [-1, 1] | ``Ca``: [-1, 1] | | | | | | | ``Cb`` : [-1, 1] | ``Cb``: [-1, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`Frohlich2017` Examples -------- >>> XYZ = np.array([0.06875297, 0.05753352, 0.02081548]) >>> ICaCb_to_XYZ(XYZ) array([ 0.20654008, 0.12197225, 0.05136951]) """ ICaCb = to_domain_1(ICaCb) LMS_prime = vector_dot(MATRIX_ICACB_LMS_TO_XYZ_2, ICaCb) with domain_range_scale("ignore"): LMS = eotf_ST2084(LMS_prime) return from_range_1(vector_dot(MATRIX_ICACB_LMS_TO_XYZ, LMS))
def eotf_BT2100_PQ(E_p): """ Defines *Recommendation ITU-R BT.2100* *Reference PQ* electro-optical transfer function (EOTF / EOCF). The EOTF maps the non-linear *PQ* signal into display light. Parameters ---------- E_p : numeric or array_like :math:`E'` denotes a non-linear colour value :math:`{R', G', B'}` or :math:`{L', M', S'}` in *PQ* space [0, 1]. Returns ------- numeric or ndarray :math:`F_D` is the luminance of a displayed linear component :math:`{R_D, G_D, B_D}` or :math:`Y_D` or :math:`I_D`, in :math:`cd/m^2`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``E_p`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``F_D`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Borer2017a`, :cite:`InternationalTelecommunicationUnion2016a` Examples -------- >>> eotf_BT2100_PQ(0.724769816665726) # doctest: +ELLIPSIS 779.9883608... """ return eotf_ST2084(E_p, 10000)
def oetf_reverse_BT2100_PQ(E_p): """ Defines *Recommendation ITU-R BT.2100* *Reference PQ* reverse opto-electrical transfer function (OETF / OECF). Parameters ---------- E_p : numeric or array_like :math:`E'` is the resulting non-linear signal (:math:`R'`, :math:`G'`, :math:`B'`). Returns ------- numeric or ndarray :math:`E = {R_S, G_S, B_S; Y_S; or I_S}` is the signal determined by scene light and scaled by camera exposure. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``E_p`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``E`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Borer2017a`, :cite:`InternationalTelecommunicationUnion2016a` Examples -------- >>> oetf_reverse_BT2100_PQ(0.724769816665726) # doctest: +ELLIPSIS 0.0999999... """ return ootf_reverse_BT2100_PQ(eotf_ST2084(E_p, 10000))
def Izazbz_to_XYZ( Izazbz: ArrayLike, constants: Optional[Structure] = None, method: Union[ Literal["Safdar 2017", "Safdar 2021", "ZCAM"], str ] = "Safdar 2017", ) -> NDArray: """ Convert from :math:`I_za_zb_z` colourspace to *CIE XYZ* tristimulus values. Parameters ---------- Izazbz :math:`I_za_zb_z` colourspace array where :math:`I_z` is the achromatic response, :math:`a_z` is redness-greenness and :math:`b_z` is yellowness-blueness. constants :math:`J_za_zb_z` colourspace constants. method Computation methods, *Safdar 2021* and *ZCAM* methods are equivalent. Returns ------- :class:`numpy.ndarray` *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. Warnings -------- The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function. Notes ----- - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``Izazbz`` | ``Iz`` : [0, 1] | ``Iz`` : [0, 1] | | | | | | | ``az`` : [-1, 1] | ``az`` : [-1, 1] | | | | | | | ``bz`` : [-1, 1] | ``bz`` : [-1, 1] | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``XYZ`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ References ---------- :cite:`Safdar2017`, :cite:`Safdar2021` Examples -------- >>> Izazbz = np.array([0.01207793, 0.00924302, 0.00526007]) >>> Izazbz_to_XYZ(Izazbz) # doctest: +ELLIPSIS array([ 0.2065401..., 0.1219723..., 0.0513696...]) """ Izazbz = as_float_array(Izazbz) method = validate_method(method, IZAZBZ_METHODS) constants = optional( constants, CONSTANTS_JZAZBZ_SAFDAR2017 if method == "safdar 2017" else CONSTANTS_JZAZBZ_SAFDAR2021, ) if method == "safdar 2017": LMS_p = vector_dot(MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P_SAFDAR2017, Izazbz) else: Izazbz[..., 0] += constants.d_0 LMS_p = vector_dot(MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P_SAFDAR2021, Izazbz) with domain_range_scale("ignore"): LMS = eotf_ST2084(LMS_p, 10000, constants) X_p_D65, Y_p_D65, Z_p_D65 = tsplit( vector_dot(MATRIX_JZAZBZ_LMS_TO_XYZ, LMS) ) X_D65 = (X_p_D65 + (constants.b - 1) * Z_p_D65) / constants.b Y_D65 = (Y_p_D65 + (constants.g - 1) * X_D65) / constants.g XYZ_D65 = tstack([X_D65, Y_D65, Z_p_D65]) return XYZ_D65
def JzAzBz_to_XYZ(JzAzBz, constants=JZAZBZ_CONSTANTS): """ Converts from :math:`J_zA_zB_z` colourspace to *CIE XYZ* tristimulus values. Parameters ---------- JzAzBz : array_like :math:`J_zA_zB_z` colourspace array where :math:`J_z` is Lightness, :math:`A_z` is redness-greenness and :math:`B_z` is yellowness-blueness. constants : Structure, optional :math:`J_zA_zB_z` colourspace constants. Returns ------- ndarray *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. Notes ----- +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``JzAzBz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | | | | | | | ``Az`` : [-1, 1] | ``Az`` : [-1, 1] | | | | | | | ``Bz`` : [-1, 1] | ``Bz`` : [-1, 1] | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+------------------+ References ---------- :cite:`Safdar2017` Examples -------- >>> JzAzBz = np.array([0.00535048, 0.00924302, 0.00526007]) >>> JzAzBz_to_XYZ(JzAzBz) # doctest: +ELLIPSIS array([ 0.2065402..., 0.1219723..., 0.0513696...]) """ J_z, A_z, B_z = tsplit(to_domain_1(JzAzBz)) I_z = ((J_z + constants.d_0) / (1 + constants.d - constants.d * (J_z + constants.d_0))) LMS_p = dot_vector(JZAZBZ_IZAZBZ_TO_LMS_P_MATRIX, tstack([I_z, A_z, B_z])) with domain_range_scale('ignore'): LMS = eotf_ST2084(LMS_p, 10000, constants) X_p_D65, Y_p_D65, Z_p_D65 = tsplit( dot_vector(JZAZBZ_LMS_TO_XYZ_MATRIX, LMS)) X_D65 = (X_p_D65 + (constants.b - 1) * Z_p_D65) / constants.b Y_D65 = (Y_p_D65 + (constants.g - 1) * X_D65) / constants.g XYZ_D65 = tstack([X_D65, Y_D65, Z_p_D65]) return from_range_1(XYZ_D65)
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. This parameter should stay at its default :math:`10000 cd/m^2` value for practical applications. It is exposed so that the definition can be used as a fitting function. Returns ------- ndarray *ITU-R BT.2020* colourspace array. Warnings -------- The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function. Notes ----- - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. +------------+-----------------------+------------------+ | **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`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ 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 = vector_dot(MATRIX_ICTCP_ICTCP_TO_LMS_P, ICTCP) with domain_range_scale('ignore'): LMS = eotf_ST2084(LMS_p, L_p) RGB = vector_dot(MATRIX_ICTCP_LMS_TO_RGB, LMS) return from_range_1(RGB)
def JzAzBz_to_XYZ(JzAzBz, constants=CONSTANTS_JZAZBZ): """ Converts from :math:`J_zA_zB_z` colourspace to *CIE XYZ* tristimulus values. Parameters ---------- JzAzBz : array_like :math:`J_zA_zB_z` colourspace array where :math:`J_z` is Lightness, :math:`A_z` is redness-greenness and :math:`B_z` is yellowness-blueness. constants : Structure, optional :math:`J_zA_zB_z` colourspace constants. Returns ------- ndarray *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. Warnings -------- The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function. Notes ----- - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``JzAzBz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | | | | | | | ``Az`` : [-1, 1] | ``Az`` : [-1, 1] | | | | | | | ``Bz`` : [-1, 1] | ``Bz`` : [-1, 1] | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``XYZ`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ References ---------- :cite:`Safdar2017` Examples -------- >>> JzAzBz = np.array([0.00535048, 0.00924302, 0.00526007]) >>> JzAzBz_to_XYZ(JzAzBz) # doctest: +ELLIPSIS array([ 0.2065402..., 0.1219723..., 0.0513696...]) """ J_z, A_z, B_z = tsplit(to_domain_1(JzAzBz)) I_z = ((J_z + constants.d_0) / (1 + constants.d - constants.d * (J_z + constants.d_0))) LMS_p = vector_dot(MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P, tstack([I_z, A_z, B_z])) with domain_range_scale('ignore'): LMS = eotf_ST2084(LMS_p, 10000, constants) X_p_D65, Y_p_D65, Z_p_D65 = tsplit( vector_dot(MATRIX_JZAZBZ_LMS_TO_XYZ, LMS)) X_D65 = (X_p_D65 + (constants.b - 1) * Z_p_D65) / constants.b Y_D65 = (Y_p_D65 + (constants.g - 1) * X_D65) / constants.g XYZ_D65 = tstack([X_D65, Y_D65, Z_p_D65]) return from_range_1(XYZ_D65)
def ICtCp_to_RGB( ICtCp: ArrayLike, method: Union[Literal["Dolby 2016", "ITU-R BT.2100-1 HLG", "ITU-R BT.2100-1 PQ", "ITU-R BT.2100-2 HLG", "ITU-R BT.2100-2 PQ", ], str, ] = "Dolby 2016", L_p: Floating = 10000, ) -> NDArray: """ Convert from :math:`IC_TC_P` colour encoding to *ITU-R BT.2020* colourspace. Parameters ---------- ICtCp :math:`IC_TC_P` colour encoding array. method Computation method. *Recommendation ITU-R BT.2100* defines multiple variants of the :math:`IC_TC_P` colour encoding: - *ITU-R BT.2100-1* - *SMPTE ST 2084:2014* inverse electro-optical transfer function (EOTF) and the :math:`IC_TC_P` matrix from :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, *ITU-R BT.2100-2 PQ* methods. - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical transfer function (OETF) and the :math:`IC_TC_P` matrix from :cite:`Dolby2016a`: *ITU-R BT.2100-1 HLG* method. - *ITU-R BT.2100-2* - *SMPTE ST 2084:2014* inverse electro-optical transfer function (EOTF) and the :math:`IC_TC_P` matrix from :cite:`Dolby2016a`: *Dolby 2016*, *ITU-R BT.2100-1 PQ*, *ITU-R BT.2100-2 PQ* methods. - *Recommendation ITU-R BT.2100* *Reference HLG* opto-electrical transfer function (OETF) and a custom :math:`IC_TC_P` matrix from :cite:`InternationalTelecommunicationUnion2018`: *ITU-R BT.2100-2 HLG* method. L_p Display peak luminance :math:`cd/m^2` for *SMPTE ST 2084:2014* non-linear encoding. This parameter should stay at its default :math:`10000 cd/m^2` value for practical applications. It is exposed so that the definition can be used as a fitting function. Returns ------- :class:`numpy.ndarray` *ITU-R BT.2020* colourspace array. Warnings -------- The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function. Notes ----- - The *ITU-R BT.2100-1 PQ* and *ITU-R BT.2100-2 PQ* methods are aliases for the *Dolby 2016* method. - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. +------------+-----------------------+------------------+ | **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`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ 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 = np.array([0.62567899, -0.01984490, 0.35911259]) >>> ICtCp_to_RGB(ICtCp, method='ITU-R BT.2100-2 HLG') # doctest: +ELLIPSIS array([ 0.4562052..., 0.0308107..., 0.0409195...]) """ ICtCp = as_float_array(ICtCp) method = validate_method( method, [ "Dolby 2016", "ITU-R BT.2100-1 HLG", "ITU-R BT.2100-1 PQ", "ITU-R BT.2100-2 HLG", "ITU-R BT.2100-2 PQ", ], ) is_hlg_method = "hlg" in method is_BT2100_2_method = "2100-2" in method LMS_p = (vector_dot(MATRIX_ICTCP_ICTCP_TO_LMS_P_HLG_BT2100_2, ICtCp) if (is_hlg_method and is_BT2100_2_method) else vector_dot( MATRIX_ICTCP_ICTCP_TO_LMS_P, ICtCp)) with domain_range_scale("ignore"): LMS = (oetf_inverse_HLG_BT2100(LMS_p) if is_hlg_method else eotf_ST2084(LMS_p, L_p)) RGB = vector_dot(MATRIX_ICTCP_LMS_TO_RGB, LMS) return RGB