There are many different colour conversions to YUV but they all have different results! Which one is officially correct?
This is the output from my test program. I have input R=128 G=50 B=50 (max value is 255). The table shows the converted YUV values, and the re-converted RGB values (which don't match the original).
./ColourConversion.exe 128 50 50
Y U V R G B Name
===============================================================================
0 0 0 128 50 50 a) Original RGB Values
79 116 162 128 50 49 b) Microsoft MSDN integer maths
78 116 162 126 49 47 c) ITU-R BT.601
73 120 162 127 49 49 d) HDTV ITU-R BT.709
73 114 202 176 25 48 e) RGB to full-range YCbCr
78 116 162 126 49 47 f) Book 'Video Demystified'
Here's my code (in C#):
using System;
namespace ColourConversion
{
class MainClass
{
//Turn on debug to enter RGB values in 'red, geen & blue'
public static bool debug = true;
public static int red = 128;
public static int green = 50;
public static int blue = 50;
//for printTidy()
public static string alphabet = "abcdefghijklmnopqrstuvwxyz";
public static int testCount = 0;
public static void Main (string[] args)
{
//init variables
int r, g, b, R, G, B, Y, U, V, C, D, E;
r = g = b = R = G = B = Y = U = V = C = D = E = 0;
//read user input if not in debug mode
if (!debug) {
if (args.Length < 3) {
//Print CLI usage
Console.WriteLine ("Enter RGB values in the format:");
Console.WriteLine ("ColourConversion.exe 255 255 255");
return;
} else {
r = clip(int.Parse (args[0]));
g = clip(int.Parse (args[1]));
b = clip(int.Parse (args[2]));
}
}else{
r = clip(red);
g = clip(green);
b = clip(blue);
}
//write table header
Console.Write("Y".PadLeft(10));
Console.Write("U".PadLeft(10));
Console.Write("V".PadLeft(10));
Console.Write("R".PadLeft(10));
Console.Write("G".PadLeft(10));
Console.Write("B".PadLeft(10));
Console.WriteLine(" Name");
Console.WriteLine("===============================================================================");
printTidy(Y,U,V,r,g,b, "Original RGB Values");
//---------------------------------------------------------------
//Microsoft MSDN integer maths
//http://msdn.microsoft.com/en-us/library/ms893078
R = r;
G = g;
B = b;
Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
C = Y - 16;
D = U - 128;
E = V - 128;
R = clip ((298 * C + 409 * E + 128) >> 8);
G = clip ((298 * C - 100 * D - 208 * E + 128) >> 8);
B = clip ((298 * C + 516 * D + 128) >> 8);
printTidy(Y,U,V,R,G,B, "Microsoft MSDN integer maths");
//---------------------------------------------------------------
//ITU-R BT.601
//http://www.equasys.de/colorconversion.html
R = r;
G = g;
B = b;
//http://www.equasys.de/equasysRGB_YCbCrColorConversionSDTV.png
Y = clip(16 + (0.257 * R + 0.504 * G + 0.098 * B));
U = clip(128 + (-0.148 * R + -0.291 * G + 0.439 * B));
V = clip(128 + (0.439 * R + -0.368 * G + -0.071 * B));
//http://www.equasys.de/equasysYCbCr_RGBColorConversionSDTV.png
R = clip(1.164 * (Y - 16) + 0.0 * (U - 128) + 1.596 * (V - 128));
G = clip(1.164 * (Y - 16) + -0.392 * (U - 128) + -0.813 * (V - 128));
B = clip(1.164 * (Y - 16) + 2.017 * (U - 128) + 0.0 * (V - 128));
printTidy(Y,U,V,R,G,B, "ITU-R BT.601");
//---------------------------------------------------------------
//HTDV ITU-R BT.709
//http://www.equasys.de/colorconversion.html
R = r;
G = g;
B = b;
//http://www.equasys.de/equasysRGB_YCbCrColorConversionHDTV.png
Y = clip(16 + ((0.183 * R) + (0.614 * G) + (0.062 * B)));
U = clip(128 + ((-0.101 * R) + (-0.339 * G) + (0.439 * B)));
V = clip(128 + ((0.439 * R) + (-0.399 * G) + (-0.040 * B)));
//http://www.equasys.de/equasysYCbCr_RGBColorConversionHDTV.png
R = clip((1.164 * (Y-16)) + (0.0 * (U-128)) + (1.793 * (V-128)));
G = clip((1.164 * (Y-16)) + (-0.213 * (U-128)) + (-0.533 * (V-128)));
B = clip((1.164 * (Y-16)) + (2.112 * (U-128)) + (0.0 * (V-128)));
printTidy(Y,U,V,R,G,B, "HDTV ITU-R BT.709");
//---------------------------------------------------------------
//RGB to full-range YCbCr
//http://www.equasys.de/colorconversion.html
R = r;
G = g;
B = b;
//http://www.equasys.de/equasysRGB_FullRangeYCbCrColorConversion.png
Y = clip(0.0 + ((0.299 * R) + (0.587 * G) + (0.114 * B)));
U = clip(128.0 + ((-0.169 * R) + (-0.331 * G) + (0.500 * B)));
V = clip(128.0 + ((0.500 * R) + (0.419 * G) + (-0.081 * R)));
//http://www.equasys.de/equasysFullRangeYCbCr_RGBColorConversion.png
R = clip((1.0 * Y) + (0.0 * (U - 128)) + (1.4 * (V - 128)));
G = clip((1.0 * Y) + (-0.343 * (U - 128)) + (-0.711 * (V - 128)));
B = clip((1.0 * Y) + (1.765 * (U - 128)) + (0.0 * (V - 128)));
printTidy(Y,U,V,R,G,B, "RGB to full-range YCbCr");
//---------------------------------------------------------------
//Book 'Video Demystified' ISBN 1-878707-09-4
//http://www.fourcc.org/fccyvrgb.php
R = r;
G = g;
B = b;
Y = clip((0.257 * R) + (0.504 * G) + (0.098 * B) + 16);
V = clip((0.439 * R) - (0.368 * G) - (0.071 * B) + 128);
U = clip(-(0.148 * R) - (0.291 * G) + (0.439 * B) + 128);
B = clip(1.164*(Y - 16) + 2.018*(U - 128));
G = clip(1.164*(Y - 16) - 0.813*(V - 128) - 0.391*(U - 128));
R = clip(1.164*(Y - 16) + 1.596*(V - 128));
printTidy(Y,U,V,R,G,B, "Book 'Video Demystified'");
}
//Print output in a nice form
public static void printTidy(int Y,int U,int V,int R,int G,int B, string methodName)
{
Console.Write(Y.ToString().PadLeft(10));
Console.Write(U.ToString().PadLeft(10));
Console.Write(V.ToString().PadLeft(10));
Console.Write(R.ToString().PadLeft(10));
Console.Write(G.ToString().PadLeft(10));
Console.Write(B.ToString().PadLeft(10));
Console.WriteLine(" " + alphabet[testCount++] + ") " + methodName);
return;
}
//overload for clip() - converts double to int
public static int clip (double d)
{
return clip((int)d);
}
//clips int to between 0 and 255
public static int clip (int i)
{
if (i < 0)
return 0;
if (i > 255)
return 255;
return i;
}
}
}