pythonopencvyuvbgr

Conversion from BGR to YUYV with OpenCV Python


I have been trying to convert a BGR captured frame into the YUYV format.

In OpenCV Python I can do convert YUYV into BGR with COLOR_YUV2BGR_YUY2 conversion code but I cannot do the reverse of this operation (there is no conversion code for this operation, I have tried COLOR_BGR2YUV but it is not converting correctly). I am curious about how to convert 3-channel BGR frame into the 2-channel YUYV frame.

Here you can see the code that I am using to change camera mode to capture YUYV and converting it into BGR, I am looking for the replacement of the cap.set(cv2.CAP_PROP_CONVERT_RGB, 0) so I can capture BGR and convert it into YUYV without cap.set(cv2.CAP_PROP_CONVERT_RGB, 0) (Because it is an optional capture setting and Windows DirectShow ignores this flag)

import cv2
import numpy as np

cap = cv2.VideoCapture(4)
_, frame = cap.read()
cap.set(cv2.CAP_PROP_CONVERT_RGB, 0)  # How to replace this line with another BGR to YUYV conversion?
_, frame_rgb_off = cap.read()
bgr_cvt = cv2.cvtColor(frame_rgb_off, cv2.COLOR_YUV2BGR_YUY2)

And here is the debugger output to show the frame contents:

Link to the screenshot

I tried to understand the YUYV format but it has only 2-channels and down sampling process is quite complicated. I have checked the YUYV to BGR conversion methods but I could not find and mathematical conversion approaches (solutions are generally using ffmpeg utility from command line but I need to do it mathematically with numpy arrays I think)


Solution

  • You can use the following code to convert your image to YUV and after that create YUYV from YUV. In this example an image is given as input to the program:

    import cv2
    import numpy as np
    
    # Load sample image
    img_bgr = cv2.imread("home.jpg")
    cv2.imshow("original", img_bgr)
    cv2.waitKey(0)
    
    # Convert from BGR to YUV
    img_yuv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2YUV)
    
    # Converting directly back from YUV to BGR results in an (almost) identical image
    img_bgr_restored = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)
    cv2.imshow("converted", img_bgr_restored)
    cv2.waitKey(0)
    diff = img_bgr.astype(np.int16) - img_bgr_restored
    print("mean/stddev diff (BGR => YUV => BGR)", np.mean(diff), np.std(diff))
    
    # Create YUYV from YUV
    y0 = np.expand_dims(img_yuv[...,0][::,::2], axis=2)
    u = np.expand_dims(img_yuv[...,1][::,::2], axis=2)
    y1 = np.expand_dims(img_yuv[...,0][::,1::2], axis=2)
    v = np.expand_dims(img_yuv[...,2][::,::2], axis=2)
    img_yuyv = np.concatenate((y0, u, y1, v), axis=2)
    img_yuyv_cvt = img_yuyv.reshape(img_yuyv.shape[0], img_yuyv.shape[1] * 2, 
    int(img_yuyv.shape[2] / 2))
    
    # Convert back to BGR results in more saturated image.
    img_bgr_restored = cv2.cvtColor(img_yuyv_cvt, cv2.COLOR_YUV2BGR_YUYV)
    cv2.imshow("converted", img_bgr_restored)
    cv2.waitKey(0)
    
    diff = img_bgr.astype(np.int16) - img_bgr_restored
    print("mean/stddev diff (BGR => YUV => YUYV => BGR)", np.mean(diff), np.std(diff))