# Pyctools - a picture processing algorithm development kit.
# http://github.com/jim-easterbrook/pyctools
# Copyright (C) 2014-20 Pyctools contributors
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
from __future__ import print_function
__all__ = ['YUVtoRGB']
__docformat__ = 'restructuredtext en'
import cv2
import numpy
from pyctools.core.config import ConfigEnum
from pyctools.core.base import Component
from pyctools.core.types import pt_float
from pyctools.components.interp.resize import resize_frame
from .matrices import Matrices
[docs]
class YUVtoRGB(Component):
"""YUV (YCbCr) to RGB converter.
Convert "YUV" (actually YCbCr) frames (with any UV subsampling) to
RGB.
For ``4:2:2`` subsampling a high quality resampling filter is used,
as specified in `BBC R&D Report 1984/04
<http://www.bbc.co.uk/rd/publications/rdreport_1984_04>`_. For other
subsampling patterns, where the correct filtering is less well
specified, simple bicubic interpolation is used.
The ``matrix`` config item chooses the matrix coefficient set. It
can be ``'601'`` ("`Rec. 601`_", standard definition) or ``'709'``
("`Rec. 709`_", high definition). In ``'auto'`` mode the matrix is
chosen according to the number of lines in the image.
WARNING: this component assumes Y input and RGB output both have
black level 0 and white level 255, not the 16..235 range specified
in Rec 601. See :py:mod:`pyctools.components.colourspace.levels` for
components to convert the Y input or RGB output. The UV input should
be in the range -112..112.
.. _Rec. 601: https://en.wikipedia.org/wiki/Rec._601
.. _Rec. 709: https://en.wikipedia.org/wiki/Rec._709
.. _YCbCr: https://en.wikipedia.org/wiki/YCbCr
"""
filter_21 = numpy.array([
-0.002913300, 0.0, 0.010153700, 0.0, -0.022357799, 0.0,
0.044929001, 0.0, -0.093861297, 0.0, 0.314049691, 0.5,
0.314049691, 0.0, -0.093861297, 0.0, 0.044929001, 0.0,
-0.022357799, 0.0, 0.010153700, 0.0, -0.002913300
], dtype=pt_float).reshape(1, -1, 1)
inputs = ['input_Y', 'input_UV'] #:
def initialise(self):
self.config['matrix'] = ConfigEnum(choices=('auto', '601', '709'))
self.last_frame_type = None
def process_frame(self):
Y_frame = self.input_buffer['input_Y'].get()
UV_frame = self.input_buffer['input_UV'].get()
out_frame = self.outframe_pool['output'].get()
out_frame.initialise(Y_frame)
if self.transform(Y_frame, UV_frame, out_frame):
self.send('output', out_frame)
else:
self.stop()
return
def transform(self, Y_frame, UV_frame, out_frame):
self.update_config()
matrix = self.config['matrix']
# check input and get data
Y = Y_frame.as_numpy()
if Y.shape[2] != 1:
self.logger.critical('Y input has %d components', Y.shape[2])
return False
UV = UV_frame.as_numpy() * pt_float(255.0 / 224.0)
if UV.shape[2] != 2:
self.logger.critical('UV input has %d components', UV.shape[2])
return False
# resample U & V
v_ss = Y.shape[0] // UV.shape[0]
h_ss = Y.shape[1] // UV.shape[1]
if h_ss == 2:
UV = resize_frame(UV, self.filter_21, 2, 1, 1, 1)
elif h_ss != 1:
UV = cv2.resize(
UV, None, fx=h_ss, fy=1, interpolation=cv2.INTER_CUBIC)
if v_ss != 1:
UV = cv2.resize(
UV, None, fx=1, fy=v_ss, interpolation=cv2.INTER_CUBIC)
# matrix to RGB
if matrix == 'auto':
matrix = ('601', '709')[Y.shape[0] > 576]
if matrix == '601':
mat = Matrices.YUVtoRGB_601
else:
mat = Matrices.YUVtoRGB_709
R = ((Y[:,:,0] * mat[0,0]) +
(UV[:,:,0] * mat[0,1]) +
(UV[:,:,1] * mat[0,2]))
G = ((Y[:,:,0] * mat[1,0]) +
(UV[:,:,0] * mat[1,1]) +
(UV[:,:,1] * mat[1,2]))
B = ((Y[:,:,0] * mat[2,0]) +
(UV[:,:,0] * mat[2,1]) +
(UV[:,:,1] * mat[2,2]))
out_frame.data = numpy.dstack((R, G, B))
out_frame.type = 'RGB'
# audit
out_frame.merge_audit({'Y': Y_frame, 'UV': UV_frame})
out_frame.set_audit(
self, 'data = YUVtoRGB(Y, UV)\n matrix: {}\n'.format(matrix))
return True