How to: Converting RGB to YUV and YUV to RGB

Recently I had to deal with converting RGB to YUV and YUV to RGB, but couldn’t find any proper documentation on how to convert back YUV to RGB. I finally got it to work after 6 hours and this is the result. I hope that this will help other people searching for this in the future.

Converting RGB to YUV

Going from RGB to YUV is rather easy, given that the ITU documents for BT.609 and BT.709 specify what needs to be multiplied with what. If you were following BT.709 you might arrive at code that looks like this (HLSL):

float4 RGBtoYUV(float4 rgba) {
	float4 yuva;
	yuva.r = rgba.r * 0.2126 + 0.7152 * rgba.g + 0.0722 * rgba.b;
	yuva.g = (rgba.b - yuva.r) / 1.8556;
	yuva.b = (rgba.r - yuva.r) / 1.5748;
	yuva.a = rgba.a;
	
	// Adjust to work on GPU
	yuva.gb += 0.5;
	
	return yuva;
}

It can further be adapted to use a matrix instead of fixed values which allows re-use of the same code and support any color space that can be expressed as a matrix, as long as the values are normalized.

float3x3 mYUV709n = { // Normalized
	0.2126, 0.7152, 0.0722,
	-0.1145721060573399, -0.3854278939426601, 0.5,
	0.5, -0.4541529083058166, -0.0458470916941834
};
float4 RGBtoYUV(float4 rgba, float3x3 yuv) {	
	return float4(
		rgba.r * yuv._m00 + rgba.g * yuv._m01 + rgba.b * yuv._m02,
		rgba.r * yuv._m10 + rgba.g * yuv._m11 + rgba.b * yuv._m12,
		rgba.r * yuv._m20 + rgba.g * yuv._m21 + rgba.b * yuv._m22,
		rgba.a
	) + float4(0,0.5,0.5,0);
}

Converting YUV to RGB

The first step to converting back from is to calculate the inverse of the matrix used to convert to YUV. There is a widget on Wolfram|Alpha if you want to do this in a browser. Then you simply multiply the YUV colors with the inverse matrix and you have your RGB output again. The code for this might look like this (HLSL):

float3x3 mYUV709i = { // Inverse Normalized
	1, 0, 1.5748,
	1, -0.187324, -0.468124,
	1, 1.8556, 0
};
float4 YUVtoRGB(float4 yuva, float3x3 yuvi) {
	yuva.gb -= 0.5;
	return float4(
		yuva.r * yuvi._m00 + yuva.g * yuvi._m01 + yuva.b * yuvi._m02,
		yuva.r * yuvi._m10 + yuva.g * yuvi._m11 + yuva.b * yuvi._m12,
		yuva.r * yuvi._m20 + yuva.g * yuvi._m21 + yuva.b * yuvi._m22,
		yuva.a);
}

This will give you proper RGB output for a given YUV input.

Further Links

Bookmark the permalink.

Comments are closed.