def test_nan_eotf_inverse_ST2084(self): """ Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_inverse_ST2084` definition nan support. """ eotf_inverse_ST2084( np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]))
def test_domain_range_scale_eotf_inverse_ST2084(self): """ Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_inverse_ST2084` definition domain and range scale support. """ C = 100 N = eotf_inverse_ST2084(C) 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_inverse_ST2084(C * factor), N * factor, decimal=7)
def test_eotf_inverse_ST2084(self): """ Tests :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_inverse_ST2084` definition. """ self.assertAlmostEqual( eotf_inverse_ST2084(0.0), 0.000000730955903, places=7) self.assertAlmostEqual( eotf_inverse_ST2084(100), 0.508078421517399, places=7) self.assertAlmostEqual( eotf_inverse_ST2084(400), 0.652578597563067, places=7) self.assertAlmostEqual(eotf_inverse_ST2084(5000, 5000), 1.0, places=7)
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 test_n_dimensional_eotf_inverse_ST2084(self): """ Test :func:`colour.models.rgb.transfer_functions.st_2084.\ eotf_inverse_ST2084` definition n-dimensional arrays support. """ C = 100 N = eotf_inverse_ST2084(C) C = np.tile(C, 6) N = np.tile(N, 6) np.testing.assert_almost_equal(eotf_inverse_ST2084(C), N, decimal=7) C = np.reshape(C, (2, 3)) N = np.reshape(N, (2, 3)) np.testing.assert_almost_equal(eotf_inverse_ST2084(C), N, decimal=7) C = np.reshape(C, (2, 3, 1)) N = np.reshape(N, (2, 3, 1)) np.testing.assert_almost_equal(eotf_inverse_ST2084(C), N, decimal=7)
def XYZ_to_ICaCb(XYZ: ArrayLike) -> NDArray: """ Convert from *CIE XYZ* tristimulus values to :math:`IC_AC_B` colourspace. Parameters ---------- XYZ *CIE XYZ* tristimulus values. Returns ------- :class:`numpy.ndarray` :math:`IC_AC_B` colourspace array. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``ICaCb`` | ``I`` : [0, 1] | ``I`` : [0, 1] | | | | | | | ``Ca`` : [-1, 1] | ``Ca``: [-1, 1] | | | | | | | ``Cb`` : [-1, 1] | ``Cb``: [-1, 1] | +------------+-----------------------+-----------------+ - Input *CIE XYZ* tristimulus values must be adapted to *CIE Standard Illuminant D Series* *D65*. References ---------- :cite:`Frohlich2017` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_ICaCb(XYZ) array([ 0.06875297, 0.05753352, 0.02081548]) """ XYZ = to_domain_1(XYZ) LMS = vector_dot(MATRIX_ICACB_XYZ_TO_LMS, XYZ) with domain_range_scale("ignore"): LMS_prime = eotf_inverse_ST2084(LMS) return from_range_1(vector_dot(MATRIX_ICACB_XYZ_TO_LMS_2, LMS_prime))
def XYZ_to_JzAzBz(XYZ_D65, constants=JZAZBZ_CONSTANTS): """ Converts from *CIE XYZ* tristimulus values to :math:`J_zA_zB_z` colourspace. Parameters ---------- XYZ_D65 : array_like *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. constants : Structure, optional :math:`J_zA_zB_z` colourspace constants. Returns ------- ndarray :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. Notes ----- +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``JzAzBz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | | | | | | | ``Az`` : [-1, 1] | ``Az`` : [-1, 1] | | | | | | | ``Bz`` : [-1, 1] | ``Bz`` : [-1, 1] | +------------+-----------------------+------------------+ References ---------- :cite:`Safdar2017` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_JzAzBz(XYZ) # doctest: +ELLIPSIS array([ 0.0053504..., 0.0092430..., 0.0052600...]) """ X_D65, Y_D65, Z_D65 = tsplit(to_domain_1(XYZ_D65)) X_p_D65 = constants.b * X_D65 - (constants.b - 1) * Z_D65 Y_p_D65 = constants.g * Y_D65 - (constants.g - 1) * X_D65 XYZ_p_D65 = tstack([X_p_D65, Y_p_D65, Z_D65]) LMS = dot_vector(JZAZBZ_XYZ_TO_LMS_MATRIX, XYZ_p_D65) with domain_range_scale('ignore'): LMS_p = eotf_inverse_ST2084(LMS, 10000, constants) I_z, A_z, B_z = tsplit(dot_vector(JZAZBZ_LMS_P_TO_IZAZBZ_MATRIX, LMS_p)) J_z = ((1 + constants.d) * I_z) / (1 + constants.d * I_z) - constants.d_0 JzAzBz = tstack([J_z, A_z, B_z]) return from_range_1(JzAzBz)
def XYZ_to_JzAzBz(XYZ_D65, constants=CONSTANTS_JZAZBZ): """ Converts from *CIE XYZ* tristimulus values to :math:`J_zA_zB_z` colourspace. Parameters ---------- XYZ_D65 : array_like *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. constants : Structure, optional :math:`J_zA_zB_z` colourspace constants. Returns ------- ndarray :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. 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. The effective domain of *SMPTE ST 2084:2014* inverse electro-optical transfer function (EOTF / EOCF) is [0.0001, 10000]. +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``XYZ`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``JzAzBz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | | | | | | | ``Az`` : [-1, 1] | ``Az`` : [-1, 1] | | | | | | | ``Bz`` : [-1, 1] | ``Bz`` : [-1, 1] | +------------+-----------------------+------------------+ References ---------- :cite:`Safdar2017` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_JzAzBz(XYZ) # doctest: +ELLIPSIS array([ 0.0053504..., 0.0092430..., 0.0052600...]) """ X_D65, Y_D65, Z_D65 = tsplit(to_domain_1(XYZ_D65)) X_p_D65 = constants.b * X_D65 - (constants.b - 1) * Z_D65 Y_p_D65 = constants.g * Y_D65 - (constants.g - 1) * X_D65 XYZ_p_D65 = tstack([X_p_D65, Y_p_D65, Z_D65]) LMS = vector_dot(MATRIX_JZAZBZ_XYZ_TO_LMS, XYZ_p_D65) with domain_range_scale('ignore'): LMS_p = eotf_inverse_ST2084(LMS, 10000, constants) I_z, A_z, B_z = tsplit(vector_dot(MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ, LMS_p)) J_z = ((1 + constants.d) * I_z) / (1 + constants.d * I_z) - constants.d_0 JzAzBz = tstack([J_z, A_z, B_z]) return from_range_1(JzAzBz)
def RGB_to_ICtCp( RGB: 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 *ITU-R BT.2020* colourspace to :math:`IC_TC_P` colour encoding. Parameters ---------- RGB *ITU-R BT.2020* colourspace 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` :math:`IC_TC_P` colour encoding 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. The effective domain of *SMPTE ST 2084:2014* inverse electro-optical transfer function (EOTF) is [0.0001, 10000]. +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``RGB`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **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_ICtCp(RGB, method='ITU-R BT.2100-2 HLG') # doctest: +ELLIPSIS array([ 0.6256789..., -0.0198449..., 0.3591125...]) """ RGB = as_float_array(RGB) 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 = vector_dot(MATRIX_ICTCP_RGB_TO_LMS, RGB) with domain_range_scale("ignore"): LMS_p = (oetf_HLG_BT2100(LMS) if is_hlg_method else eotf_inverse_ST2084(LMS, L_p)) ICtCp = (vector_dot(MATRIX_ICTCP_LMS_P_TO_ICTCP_HLG_BT2100_2, LMS_p) if (is_hlg_method and is_BT2100_2_method) else vector_dot( MATRIX_ICTCP_LMS_P_TO_ICTCP, LMS_p)) return ICtCp
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. 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 :math:`IC_TC_P` colour encoding 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. The effective domain of *SMPTE ST 2084:2014* inverse electro-optical transfer function (EOTF / EOCF) is [0.0001, 10000]. +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``RGB`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **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 = vector_dot(MATRIX_ICTCP_RGB_TO_LMS, RGB) with domain_range_scale('ignore'): LMS_p = eotf_inverse_ST2084(LMS, L_p) ICTCP = vector_dot(MATRIX_ICTCP_LMS_P_TO_ICTCP, LMS_p) return from_range_1(ICTCP)
def XYZ_to_Izazbz( XYZ_D65: ArrayLike, constants: Optional[Structure] = None, method: Union[ Literal["Safdar 2017", "Safdar 2021", "ZCAM"], str ] = "Safdar 2017", ) -> NDArray: """ Convert from *CIE XYZ* tristimulus values to :math:`I_za_zb_z` colourspace. Parameters ---------- XYZ_D65 *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. constants :math:`J_za_zb_z` colourspace constants. method Computation methods, *Safdar 2021* and *ZCAM* methods are equivalent. Returns ------- :class:`numpy.ndarray` :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. 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. The effective domain of *SMPTE ST 2084:2014* inverse electro-optical transfer function (EOTF) is [0.0001, 10000]. +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``XYZ`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``Izazbz`` | ``Iz`` : [0, 1] | ``Iz`` : [0, 1] | | | | | | | ``az`` : [-1, 1] | ``az`` : [-1, 1] | | | | | | | ``bz`` : [-1, 1] | ``bz`` : [-1, 1] | +------------+-----------------------+------------------+ References ---------- :cite:`Safdar2017`, :cite:`Safdar2021` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_Izazbz(XYZ) # doctest: +ELLIPSIS array([ 0.0120779..., 0.0092430..., 0.0052600...]) """ X_D65, Y_D65, Z_D65 = tsplit(as_float_array(XYZ_D65)) method = validate_method(method, IZAZBZ_METHODS) constants = optional( constants, CONSTANTS_JZAZBZ_SAFDAR2017 if method == "safdar 2017" else CONSTANTS_JZAZBZ_SAFDAR2021, ) X_p_D65 = constants.b * X_D65 - (constants.b - 1) * Z_D65 Y_p_D65 = constants.g * Y_D65 - (constants.g - 1) * X_D65 XYZ_p_D65 = tstack([X_p_D65, Y_p_D65, Z_D65]) LMS = vector_dot(MATRIX_JZAZBZ_XYZ_TO_LMS, XYZ_p_D65) with domain_range_scale("ignore"): LMS_p = eotf_inverse_ST2084(LMS, 10000, constants) if method == "safdar 2017": Izazbz = vector_dot(MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2017, LMS_p) else: Izazbz = vector_dot(MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ_SAFDAR2021, LMS_p) Izazbz[..., 0] -= constants.d_0 return Izazbz