rgb to yuv conversion and accessing Y, U and V channels

enterbutton picture enterbutton · May 15, 2017 · Viewed 18.9k times · Source

I have been looking this conversion for a while. What are the ways of converting RGB image to YUV image and accessing Y, U and V channels using Python on Linux? (using opencv, skimage, or etc...)

Update: I used opencv

img_yuv = cv2.cvtColor(image, cv2.COLOR_BGR2YUV)
y, u, v = cv2.split(img_yuv)

cv2.imshow('y', y)
cv2.imshow('u', u)
cv2.imshow('v', v)
cv2.waitKey(0)

and got this result but they are all seems gray. Couldn't get an result represented like on the wikipedia page

Am I doing something wrong?

enter image description here

Answer

Dan Mašek picture Dan Mašek · May 15, 2017

NB: The YUV <-> RGB conversions in OpenCV versions prior to 3.2.0 are buggy! For one, in many cases the order of U and V channels was swapped. As far as I can tell, 2.x is still broken as of 2.4.13.2 release.


The reason they appear grayscale is that in splitting the 3-channel YUV image you created three 1-channel images. Since the data structures that contain the pixels do not store any information about what the values represent, imshow treats any 1-channel image as grayscale for display. Similarly, it would treat any 3-channel image as BGR.

What you see in the Wikipedia example is a false color rendering of the chrominance channels. In order to achieve this, you need to either apply a pre-defined colormap or use a custom look-up table (LUT). This will map the U and V values to appropriate BGR values which can then be displayed.

As it turns out, the colormaps used for the Wikipedia example are rather simple.

Colormap for U channel

Simple progression between green and blue:

colormap_u = np.array([[[i,255-i,0] for i in range(256)]],dtype=np.uint8)

Colormap for U channel

Colormap for V channel

Simple progression between green and red:

colormap_v = np.array([[[0,255-i,i] for i in range(256)]],dtype=np.uint8)

Colormap for V channel

Visualizing YUV Like the Example

Now, we can put it all together, to recreate the example:

import cv2
import numpy as np


def make_lut_u():
    return np.array([[[i,255-i,0] for i in range(256)]],dtype=np.uint8)

def make_lut_v():
    return np.array([[[0,255-i,i] for i in range(256)]],dtype=np.uint8)


img = cv2.imread('shed.png')

img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
y, u, v = cv2.split(img_yuv)

lut_u, lut_v = make_lut_u(), make_lut_v()

# Convert back to BGR so we can apply the LUT and stack the images
y = cv2.cvtColor(y, cv2.COLOR_GRAY2BGR)
u = cv2.cvtColor(u, cv2.COLOR_GRAY2BGR)
v = cv2.cvtColor(v, cv2.COLOR_GRAY2BGR)

u_mapped = cv2.LUT(u, lut_u)
v_mapped = cv2.LUT(v, lut_v)

result = np.vstack([img, y, u_mapped, v_mapped])

cv2.imwrite('shed_combo.png', result)

Result:

Composite of original and Y, U, V channels