0

Currently I'm working with some YUV422p-formatted pics. I read the documentations and tried running python codes below:

print([i for i in dir(cv2) if i.startswith("COLOR_YUV")])

And I found no support for YUV422p format. Does anybody know the reason why opencv has no support for such format, or if there is any other python-based libraries that provides such support?

bhuguu
  • 1
  • 1
    There is no in-built support, have a look at [this SO post](https://stackoverflow.com/questions/60889484/opencv-convert-rgb-array-to-yuv422-in-python) – Jeru Luke Aug 09 '22 at 06:30
  • But what is the file format ? (Tiff, Png ?) If the image is already in memory, consider COLOR_YUV2BGR_YUYV. –  Aug 09 '22 at 08:21

1 Answers1

0

We may use PyAV library.
PyAV is a Pythonic binding for the FFmpeg libraries.

FFmpeg supports YUV422 Planar picture format, and PyAV also supports it.
Unlike other FFmpeg bindings, PyAV is relatively low-level - instead of using FFmpeg CLI, it allows more low level functionalities that supported by the C interface of FFmpeg.

libswscale is "sub module" of FFmpeg that supports multiple color formats conversions.
PyAV exposes some of the capabilities of libswscale.


Start by creating raw input file in yuv422p pixel format, using FFmpeg CLI.

Execute the following shell command for building sample input (used for testing):

ffmpeg -y -f lavfi -i testsrc=192x108:rate=1:duration=10 -vf "scale=out_color_matrix=bt709:out_range=tv" -pix_fmt yuv422p -f rawvideo input.yuv422p


The next example, uses PyAV for converting from raw yuv422p to BGR pixel format:

  • Create a VideoFrame object:

     frame = VideoFrame(cols, rows, 'yuv422p')
    
  • Assume y, u and v are 3 color planes (y is full resolution, and u and v are half resolution in the horizontal axis).
    The planes may be stored in NumPy arrays or bytes arrays (multiple buffer formats are supported).
    Update the 3 planes of frame:

     frame.planes[0].update(y)
     frame.planes[1].update(u)
     frame.planes[2].update(v)
    

Convert the frame to BGR format using reformat method, and convert to NumPy array:

    bgr_frame = frame.reformat(cols, rows, 'bgr24', src_colorspace='ITU709', dst_colorspace='DEFAULT', interpolation='BILINEAR')
    bgr = bgr_frame.to_ndarray()

The following code sample reads yuv422p raw data from file, converts to BRG using PyAV (and shows the output using OpenCV):

import cv2
from av.video.frame import VideoFrame

# Build raw input file using shell command:  
# ffmpeg -y -f lavfi -i testsrc=192x108:rate=1:duration=10 -vf "scale=out_color_matrix=bt709:out_range=tv" -pix_fmt yuv422p -f rawvideo input.yuv422p

cols, rows, n_frames = 192, 108, 10

frame = VideoFrame(cols, rows, 'yuv422p')

with open('input.yuv422p', 'rb') as f:
    for n in range(n_frames):
        y = f.read(cols*rows)  # y = np.frombuffer(f.read(cols*rows), np.uint8).reshape(rows, cols)
        u = f.read(cols*rows//2)  # u = np.frombuffer(f.read(cols*rows//2), np.uint8).reshape(rows, cols//2)
        v = f.read(cols*rows//2)  # v = np.frombuffer(f.read(cols*rows//2), np.uint8).reshape(rows, cols//2)
        frame.planes[0].update(y)
        frame.planes[1].update(u)
        frame.planes[2].update(v)

        bgr_frame = frame.reformat(cols, rows, 'bgr24', src_colorspace='ITU709', dst_colorspace='DEFAULT', interpolation='BILINEAR')

        bgr = bgr_frame.to_ndarray()

        cv2.imshow('bgr', bgr)
        cv2.waitKey(1000)

cv2.destroyAllWindows()

Notes:
Installing PyAV may be problematic (at least when using Windows).
Instead of using pip install, I downloaded a whl file from here (and used pip install for installing the wheel).

I assumed that the YUV applies BT.709 standard (and applies "TV range" - Y range is [16, 235] not [0, 255]).
For correct conversion, you have to know the exacts standard of your raw data.

Rotem
  • 30,366
  • 4
  • 32
  • 65