def test_CV_range(self): """ Tests :func:`colour.models.rgb.transfer_functions.common.CV_range` definition. """ np.testing.assert_array_equal(CV_range(8, True, True), np.array([16, 235])) np.testing.assert_array_equal(CV_range(8, False, True), np.array([0, 255])) np.testing.assert_almost_equal(CV_range(8, True, False), np.array([0.06274510, 0.92156863]), decimal=7) np.testing.assert_array_equal(CV_range(8, False, False), np.array([0, 1])) np.testing.assert_array_equal(CV_range(10, True, True), np.array([64, 940])) np.testing.assert_array_equal(CV_range(10, False, True), np.array([0, 1023])) np.testing.assert_almost_equal(CV_range(10, True, False), np.array([0.06256109, 0.91886608]), decimal=7) np.testing.assert_array_equal(CV_range(10, False, False), np.array([0, 1]))
def YCbCr_to_RGB(YCbCr, K=WEIGHTS_YCBCR['ITU-R BT.709'], in_bits=8, in_legal=True, in_int=False, out_bits=10, out_legal=False, out_int=False, **kwargs): """ Converts an array of *Y'CbCr* colour encoding values to the corresponding *R'G'B'* values array. Parameters ---------- YCbCr : array_like Input *Y'CbCr* colour encoding array of integer or float values. K : array_like, optional Luma weighting coefficients of red and blue. See :attr:`colour.WEIGHTS_YCBCR` for presets. Default is *(0.2126, 0.0722)*, the weightings for *ITU-R BT.709*. in_bits : int, optional Bit depth for integer input, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Default is *8*. in_legal : bool, optional Whether to treat the input values as legal range. Default is *True*. in_int : bool, optional Whether to treat the input values as ``in_bits`` integer code values. Default is *False*. out_bits : int, optional Bit depth for integer output, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Ignored if ``out_legal`` and ``out_int`` are both *False*. Default is *10*. out_legal : bool, optional Whether to return legal range values. Default is *False*. out_int : bool, optional Whether to return values as ``out_bits`` integer code values. Default is *False*. Other Parameters ---------------- in_range : array_like, optional Array overriding the computed range such as *in_range = (Y_min, Y_max, C_min, C_max)*. If ``in_range`` is undefined, *Y_min*, *Y_max*, *C_min* and *C_max* will be computed using :func:`colour.models.rgb.ycbcr.YCbCr_ranges` definition. out_range : array_like, optional Array overriding the computed range such as *out_range = (RGB_min, RGB_max)*. If ``out_range`` is undefined, *RGB_min* and *RGB_max* will be computed using :func:`colour.CV_range` definition. Returns ------- ndarray *R'G'B'* array of integer or float values. Notes ----- +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ | ``YCbCr`` | [0, 1] | [0, 1] | +----------------+-----------------------+---------------+ +----------------+-----------------------+---------------+ | **Range \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ | ``RGB`` | [0, 1] | [0, 1] | +----------------+-----------------------+---------------+ \\* This definition has input and output integer switches, thus the domain-range scale information is only given for the floating point mode. Warnings -------- For *Recommendation ITU-R BT.2020*, :func:`colour.YCbCr_to_RGB` definition is only applicable to the non-constant luminance implementation. :func:`colour.YcCbcCrc_to_RGB` definition should be used for the constant luminance case as per :cite:`InternationalTelecommunicationUnion2015h`. References ---------- :cite:`InternationalTelecommunicationUnion2011e`, :cite:`InternationalTelecommunicationUnion2015i`, :cite:`SocietyofMotionPictureandTelevisionEngineers1999b`, :cite:`Wikipedia2004d` Examples -------- >>> YCbCr = np.array([502, 512, 512]) >>> YCbCr_to_RGB(YCbCr, in_bits=10, in_legal=True, in_int=True) array([ 0.5, 0.5, 0.5]) """ if in_int: YCbCr = as_float_array(YCbCr) else: YCbCr = to_domain_1(YCbCr) Y, Cb, Cr = tsplit(YCbCr.astype(DEFAULT_FLOAT_DTYPE)) Kr, Kb = K Y_min, Y_max, C_min, C_max = kwargs.get( 'in_range', YCbCr_ranges(in_bits, in_legal, in_int)) RGB_min, RGB_max = kwargs.get('out_range', CV_range(out_bits, out_legal, out_int)) Y -= Y_min Cb -= (C_max + C_min) / 2 Cr -= (C_max + C_min) / 2 Y *= 1 / (Y_max - Y_min) Cb *= 1 / (C_max - C_min) Cr *= 1 / (C_max - C_min) R = Y + (2 - 2 * Kr) * Cr B = Y + (2 - 2 * Kb) * Cb G = (Y - Kr * R - Kb * B) / (1 - Kr - Kb) RGB = tstack([R, G, B]) RGB *= RGB_max - RGB_min RGB += RGB_min RGB = np.round(RGB).astype(DEFAULT_INT_DTYPE) if out_int else from_range_1( RGB) return RGB
def RGB_to_YCbCr(RGB, K=WEIGHTS_YCBCR['ITU-R BT.709'], in_bits=10, in_legal=False, in_int=False, out_bits=8, out_legal=True, out_int=False, **kwargs): """ Converts an array of *R'G'B'* values to the corresponding *Y'CbCr* colour encoding values array. Parameters ---------- RGB : array_like Input *R'G'B'* array of floats or integer values. K : array_like, optional Luma weighting coefficients of red and blue. See :attr:`colour.WEIGHTS_YCBCR` for presets. Default is *(0.2126, 0.0722)*, the weightings for *ITU-R BT.709*. in_bits : int, optional Bit depth for integer input, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Default is *10*. in_legal : bool, optional Whether to treat the input values as legal range. Default is *False*. in_int : bool, optional Whether to treat the input values as ``in_bits`` integer code values. Default is *False*. out_bits : int, optional Bit depth for integer output, or used in the calculation of the denominator for legal range float values, i.e. 8-bit means the float value for legal white is *235 / 255*. Ignored if ``out_legal`` and ``out_int`` are both *False*. Default is *8*. out_legal : bool, optional Whether to return legal range values. Default is *True*. out_int : bool, optional Whether to return values as ``out_bits`` integer code values. Default is *False*. Other Parameters ---------------- in_range : array_like, optional Array overriding the computed range such as *in_range = (RGB_min, RGB_max)*. If ``in_range`` is undefined, *RGB_min* and *RGB_max* will be computed using :func:`colour.CV_range` definition. out_range : array_like, optional Array overriding the computed range such as *out_range = (Y_min, Y_max, C_min, C_max)`. If ``out_range`` is undefined, *Y_min*, *Y_max*, *C_min* and *C_max* will be computed using :func:`colour.models.rgb.ycbcr.YCbCr_ranges` definition. Returns ------- ndarray *Y'CbCr* colour encoding array of integer or float values. Warnings -------- For *Recommendation ITU-R BT.2020*, :func:`colour.RGB_to_YCbCr` definition is only applicable to the non-constant luminance implementation. :func:`colour.RGB_to_YcCbcCrc` definition should be used for the constant luminance case as per :cite:`InternationalTelecommunicationUnion2015h`. Notes ----- +----------------+-----------------------+---------------+ | **Domain \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ | ``RGB`` | [0, 1] | [0, 1] | +----------------+-----------------------+---------------+ +----------------+-----------------------+---------------+ | **Range \\*** | **Scale - Reference** | **Scale - 1** | +================+=======================+===============+ | ``YCbCr`` | [0, 1] | [0, 1] | +----------------+-----------------------+---------------+ \\* This definition has input and output integer switches, thus the domain-range scale information is only given for the floating point mode. - The default arguments, ``**{'in_bits': 10, 'in_legal': False, 'in_int': False, 'out_bits': 8, 'out_legal': True, 'out_int': False}`` transform a float *R'G'B'* input array normalised to domain [0, 1] (``in_bits`` is ignored) to a float *Y'CbCr* output array where *Y'* is normalised to range [16 / 255, 235 / 255] and *Cb* and *Cr* are normalised to range [16 / 255, 240./255]. The float values are calculated based on an [0, 255] integer range, but no 8-bit quantisation or clamping are performed. References ---------- :cite:`InternationalTelecommunicationUnion2011e`, :cite:`InternationalTelecommunicationUnion2015i`, :cite:`SocietyofMotionPictureandTelevisionEngineers1999b`, :cite:`Wikipedia2004d` Examples -------- >>> RGB = np.array([1.0, 1.0, 1.0]) >>> RGB_to_YCbCr(RGB) # doctest: +ELLIPSIS array([ 0.9215686..., 0.5019607..., 0.5019607...]) Matching float output of The Foundry Nuke's Colorspace node set to YCbCr: >>> RGB_to_YCbCr(RGB, ... out_range=(16 / 255, 235 / 255, 15.5 / 255, 239.5 / 255)) ... # doctest: +ELLIPSIS array([ 0.9215686..., 0.5 , 0.5 ]) Matching float output of The Foundry Nuke's Colorspace node set to YPbPr: >>> RGB_to_YCbCr(RGB, out_legal=False, out_int=False) ... # doctest: +ELLIPSIS array([ 1., 0., 0.]) Creating integer code values as per standard 10-bit SDI: >>> RGB_to_YCbCr(RGB, out_legal=True, out_bits=10, out_int=True) ... # doctest: +ELLIPSIS array([940, 512, 512]...) For JFIF JPEG conversion as per ITU-T T.871 :cite:`InternationalTelecommunicationUnion2011e`: >>> RGB = np.array([102, 0, 51]) >>> RGB_to_YCbCr(RGB, K=WEIGHTS_YCBCR['ITU-R BT.601'], in_range=(0, 255), ... out_range=(0, 255, 0, 256), out_int=True) ... # doctest: +ELLIPSIS array([ 36, 136, 175]...) Note the use of 256 for the max *Cb / Cr* value, which is required so that the *Cb* and *Cr* output is centered about 128. Using 255 centres it about 127.5, meaning that there is no integer code value to represent achromatic colours. This does however create the possibility of output integer codes with value of 256, which cannot be stored in 8-bit integer representation. Recommendation ITU-T T.871 specifies these should be clamped to 255. These JFIF JPEG ranges are also obtained as follows: >>> RGB_to_YCbCr(RGB, K=WEIGHTS_YCBCR['ITU-R BT.601'], in_bits=8, ... in_int=True, out_legal=False, out_int=True) ... # doctest: +ELLIPSIS array([ 36, 136, 175]...) """ if in_int: RGB = as_float_array(RGB) else: RGB = to_domain_1(RGB) Kr, Kb = K RGB_min, RGB_max = kwargs.get('in_range', CV_range(in_bits, in_legal, in_int)) Y_min, Y_max, C_min, C_max = kwargs.get( 'out_range', YCbCr_ranges(out_bits, out_legal, out_int)) RGB_float = RGB.astype(DEFAULT_FLOAT_DTYPE) - RGB_min RGB_float *= 1 / (RGB_max - RGB_min) R, G, B = tsplit(RGB_float) Y = Kr * R + (1 - Kr - Kb) * G + Kb * B Cb = 0.5 * (B - Y) / (1 - Kb) Cr = 0.5 * (R - Y) / (1 - Kr) Y *= Y_max - Y_min Y += Y_min Cb *= C_max - C_min Cr *= C_max - C_min Cb += (C_max + C_min) / 2 Cr += (C_max + C_min) / 2 YCbCr = tstack([Y, Cb, Cr]) YCbCr = np.round(YCbCr).astype( DEFAULT_INT_DTYPE) if out_int else from_range_1(YCbCr) return YCbCr