Conversion between RGB and YUV color space (2024)

principle

Correspondence between RGB and YUV space

According to the relevant knowledge of TV principles, the corresponding relationship between RGB and YUV is:
{Y=0.299R+0.587G+0.114BU=0.1684R0.3316G+0.5B=0.564(BY)V=0.5R0.4187G0.0813B=0.713(RY)(1)\begin{cases} Y= 0.299\ R &+ 0.587\ G &+ 0.114\ B \\ U= -0.1684\ R &- 0.3316\ G &+ 0.5\ B &= 0.564\ (B-Y) \\ V= 0.5\ R &- 0.4187\ G &- 0.0813\ B &= 0.713\ (R-Y) \\ \end{cases} \tag{1}Y=0.299RU=0.1684RV=0.5R+0.587G0.3316G0.4187G+0.114B+0.5B0.0813B=0.564(BY)=0.713(RY)(1)
Among them, in order to control the dynamic range of the color difference signal at [-0.5, 0.5], the normalization process before quantization is required, and the compression coefficient of the digital color difference signal (0.564 respectively) With 0.713).

Quantization level allocation

Reference: Section 7.4.2 of "Principles of Modern Television": Distribution of quantization levels of video signals

When performing 8-bit quantization, a certain margin needs to be left at the upper and lower ends as a guard band for the signal to exceed the dynamic range. specifically:

  • For the luminance signal, set aside 20 levels at the upper end of the 256 levels, and leave 16 levels at the lower end as a margin, namelyThe dynamic range of Y is 16-235
  • For two color-difference signals, leave 15 levels at the upper end of the 256 levels, and leave 16 levels at the lower end as a margin, namelyThe dynamic range of U and V is 16-240

According to the code level digital expression
the amountTransformWaitlevel=int{QmaxQminmoldTo beElectricitylevelmostBigvaluemoldTo beElectricitylevelmostsmallvalue×CorrectshouldofnumberwordElectricitylevelpublicformula+0ElectricitylevelCorrectshouldGetthe amountTransformWaitlevel}(2)Quantization level={\rm{int}}\left\{ \dfrac{Q_{\max}-Q_{\min}}{Maximum analog level-Minimum analog level Value)\times corresponds to the digital level formula +0 level corresponds to the quantization level\right\} \tag{2}the amountTransformWaitlevel=int{moldTo beElectricitylevelmostBigvaluemoldTo beElectricitylevelmostsmallvalueQmaxQmin×CorrectshouldofnumberwordElectricitylevelpublicformula+0ElectricitylevelCorrectshouldGetthe amountTransformWaitlevel}(2)
knows
{Y=int{235160.5(0.5)Y+16}U=int{2401610U+128}V=int{2401610V+128}(3)\begin{cases} Y' = {\rm int}\left\{\dfrac {235-16}{0.5-(-0.5)}Y+16 \right\}\\ U' = {\rm int}\left\{\dfrac {240-16}{1-0}U+128 \right\}\\ V' = {\rm int}\left\{\dfrac {240-16}{1-0}V+128 \right\} \end{cases} \tag{3}Y=int{0.5(0.5)23516Y+16}U=int{1024016U+128}V=int{1024016V+128}(3)
where,

  • int{\rm int}intIndicates rounding down;
  • YY'YUU'UVV'VIs the digital quantization level,YYYUUUVVVIs the normalized analog level (Y[0,1]Y\in [0,1]Y[0,1]U,V[0.5,0.5]U,V\in [-0.5,0.5]U,V[0.5,0.5]);
  • Considering that the color difference signal has a negative value, the original value of 0 needs to be corresponded to 128, so 128 is added.

Since the read RGB file has been quantized with 8 bit (the range of the three RGB components is 0-255), the formula(2)(2)(2)Make corrections, firstYYYMapped to -0.5—0.5,UUUVVVMapped to 0-1:
{Y=int{219255Y+16}U=int{224255U+128}V=int{224255V+128}(4)\begin{cases} Y' = {\rm int}\left\{ \dfrac {219}{255}Y+16 \right\}\\ U' = {\rm int}\left\{ \dfrac {224}{255}U+128 \right\}\\ V' = {\rm int}\left\{ \dfrac {224}{255}V+128 \right\} \end{cases} \tag{4}Y=int{255219Y+16}U=int{255224U+128}V=int{255224V+128}(4)
bring in(1)(1)(1)Formula, get:
{Y=66R+129G+25B255+16U=38R74G+112B255+128V=112R94G18B255+128(5)\begin{cases} Y= \dfrac {66R + 129G + 25B}{255} + 16 \\ U= \dfrac{-38R - 74G + 112B}{255} +128 \\ V= \dfrac{112R - 94G - 18B}{255} + 128 \end{cases} \tag{5}Y=25566R+129G+25B+16U=25538R74G+112B+128V=255112R94G18B+128(5)

In order to improve the calculation efficiency of the computer without causing excessive errors, use in the program>> 8Instead of dividing by 255.

will(5)(5)(5)The formula is written in matrix form:
[YUV]=1255[661292538741121129418][RGB]+[1616128](6)\begin{bmatrix} Y \\ U \\ V \end{bmatrix} = \dfrac {1}{255} \begin{bmatrix} 66 & 129 & 25 \\ -38 & -74 & 112 \\ 112 & -94 & -18 \end{bmatrix} \begin{bmatrix} R \\ G \\ B \end{bmatrix} + \begin{bmatrix} 16 \\ 16 \\ 128 \end{bmatrix} \tag{6}YUV=2551663811212974942511218RGB+1616128(6)

And rememberA=[661292538741121129418]\boldsymbol A= \begin{bmatrix} 66 & 129 & 25 \\ -38 & -74 & 112 \\ 112 & -94 & -18 \end{bmatrix}A=663811212974942511218

Reverse solution, get:
[RGB]=255AT[Y16U16V128](7)\begin{bmatrix} R \\ G \\ B \end{bmatrix} = 255\boldsymbol A^{\rm T} \begin{bmatrix} Y-16 \\ U-16 \\ V-128 \end{bmatrix} \tag{7}RGB=255ATY16U16V128(7)
Organized into:

R = (298 * Y + 411 * V - 57344) >> 8;G = (298 * Y - 101 * U - 211 * V + 34739) >> 8;B = (298 * Y + 519 * U - 71117) >> 8;

mainCommand line parameters of the function

mainThe function actually has two formal parameters,int argcwithchar* argv[]. Although it is the default in many cases, in operations involving files, for example, using command line parameters can provide certain convenience for programming.

The setting method is as follows: In Visual Studio, click Project→Project Properties in the menu bar in turn, and click "Debug" under the Configuration Properties menu of the project properties page. Set the working directory by browsing the folder and enter it in the command parameternStrings (separated by spaces).

Conversion between RGB and YUV color space (1)

Command line parameter setting

These strings will be automatically passed toargv, As its first1tonElements (th0Elements are"Project name.exe"),andargcThe value isn+1

Source code

declarations.h

#pragma oncevoid rgb2yuv(FILE*, int, int, int, unsigned char*, unsigned char*, unsigned char*, unsigned char*);void yuv2rgb(FILE*, int, int, int, unsigned char*, unsigned char*, unsigned char*, unsigned char*);void errorData(int, unsigned char*, char* []);

rgb2yuv.cpp

#include <iostream>#include "declarations.h"int rgb66[256], rgb129[256], rgb25[256];int rgb38[256], rgb74[256], rgb112[256];int rgb94[256], rgb18[256];void rgbLookupTable(){for (int i = 0; i < 256; i++){rgb66[i] = 66 * i;rgb129[i] = 129 * i;rgb25[i] = 25 * i;rgb38[i] = 38 * i;rgb74[i] = 74 * i;rgb112[i] = 112 * i;rgb94[i] = 94 * i;rgb18[i] = 18 * i;}}void rgb2yuv(FILE* yuvFile, int rgbSize, int w, int h, unsigned char* rgbBuf, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf){unsigned char* uBuf444 = NULL;// U component buffer before downsamplingunsigned char* vBuf444 = NULL;// V component buffer before downsamplinguBuf444 = new unsigned char[rgbSize / 3];// 4:4:4 formatvBuf444 = new unsigned char[rgbSize / 3];int pxNum = w * h;// RGB to YUV (4:4:4)for (int i = 0; i < pxNum; i++)// i is the image pixel number{unsigned char r = rgbBuf[3 * i + 2];// R component of the i-th pixel of the RGB imageunsigned char g = rgbBuf[3 * i + 1];// G component of the i-th pixel of the RGB imageunsigned char b = rgbBuf[3 * i];// The B component of the i-th pixel of the RGB image//yBuf[i] = ((66 * r + 129 * g + 25 * b) >> 8) + 16;//uBuf444[i] = ((-38 * r - 74 * g + 112 * b) >> 8) + 128;//vBuf444[i] = ((112 * r - 94 * g - 18 * b) >> 8) + 128;rgbLookupTable();// Use lookup table to improve computing efficiencyyBuf[i] = ((rgb66[r] + rgb129[g] + rgb25[b]) >> 8) + 16;uBuf444[i] = ((-rgb38[r] - rgb74[g] + rgb112[b]) >> 8) + 128;vBuf444[i] = ((rgb112[r] - rgb94[g] - rgb18[b]) >> 8) + 128;}// 4:4:4 to 4:2:0for (int i = 0; i < h; i += 2){for (int j = 0; j < w; j += 2){uBuf[i / 2 * w / 2 + j / 2] = uBuf444[i * w + j];vBuf[i / 2 * w / 2 + j / 2] = vBuf444[i * w + j];}}delete[]uBuf444;delete[]vBuf444;fwrite(yBuf, sizeof(unsigned char), rgbSize / 3, yuvFile);fwrite(uBuf, sizeof(unsigned char), rgbSize / 12, yuvFile);fwrite(vBuf, sizeof(unsigned char), rgbSize / 12, yuvFile);}

yuv2rgb.cpp

#include <iostream>#include "declarations.h"int yuv298[256], yuv411[256];int yuv101[256], yuv211[256];int yuv519[256];void yuvLookupTable(){for (int i = 0; i < 256; i++){yuv298[i] = 298 * i;yuv411[i] = 411 * i;yuv101[i] = 101 * i;yuv211[i] = 211 * i;yuv519[i] = 519 * i;}}void yuv2rgb(FILE* rgbFile, int yuvSize, int w, int h, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf, unsigned char* rgbBuf){unsigned char* uBuf444 = new unsigned char[yuvSize * 2 / 3];// Revert to 4:4:4 U component bufferunsigned char* vBuf444 = new unsigned char[yuvSize * 2 / 3];// Revert to 4:4:4 V component bufferint pxNum = w * h;// Total number of pixels in the image// 4:2:0 to 4:4:4for (int i = 0; i < h / 2; i++)// i control line{for (int j = 0; j < w / 2; j++)// j control column{uBuf444[2 * i * w + 2 * j] = uBuf[i * w / 2 + j];uBuf444[2 * i * w + 2 * j + 1] = uBuf[i * w / 2 + j];uBuf444[2 * i * w + 2 * j + w] = uBuf[i * w / 2 + j];uBuf444[2 * i * w + 2 * j + w + 1] = uBuf[i * w / 2 + j];vBuf444[2 * i * w + 2 * j] = vBuf[i * w / 2 + j];vBuf444[2 * i * w + 2 * j + 1] = vBuf[i * w / 2 + j];vBuf444[2 * i * w + 2 * j + w] = vBuf[i * w / 2 + j];vBuf444[2 * i * w + 2 * j + w + 1] = vBuf[i * w / 2 + j];}}// YUV (4:4:4) to RGBfor (int i = 0; i < pxNum; i++){// All intermediate variables use int type to ensure sufficient accuracy and prevent overflowint y = yBuf[i];// The Y component of the i-th pixel of the YUV imageint u = uBuf444[i];// U component of the i-th pixel of YUV image (4:4:4)int v = vBuf444[i];// The V component of the i-th pixel of the YUV image (4:4:4)int r;int g;int b;yuvLookupTable();//r = (298 * y + 411 * v-57344) >> 8; // R component of the restored RGB imager = (yuv298[y] + yuv411[v] - 57344) >> 8;// R component of the restored RGB imageif (r < 0)r = 0;// fixif (r > 255)r = 255;//g = (298 * y-101 * u-211 * v + 34739) >> 8; // G component of the restored RGB imageg = (yuv298[y] - yuv101[u] - yuv211[v] + 34739) >> 8;// G component of the restored RGB imageif (g < 0)g = 0;if (g > 255)g = 255;//b = (298 * y + 519 * u-71117) >> 8; // The B component of the restored RGB imageb = (yuv298[y] + yuv519[u] - 71117) >> 8;// The B component of the restored RGB imageif (b < 0)b = 0;if (b > 255)b = 255;rgbBuf[3 * i + 2] = (unsigned char)r;// R component of the restored RGB imagergbBuf[3 * i + 1] = (unsigned char)g;// G component of the restored RGB imagergbBuf[3 * i] = (unsigned char)b;// The B component of the restored RGB image}delete[]uBuf444;delete[]vBuf444;fwrite(rgbBuf, sizeof(unsigned char), yuvSize * 2, rgbFile);}

errorData.cpp

#include <iostream>#include "declarations.h"using namespace std;void errorData(int yuvSize, unsigned char* rgbBuf, char* argv[]){FILE* rgbOriFile = NULL;// Original RGB image file pointerFILE* errorFile = NULL;// Error data file pointerconst char* rgbOriName = argv[1];// Original RGB image file nameconst char* errorName = argv[4];// Error data file name// open a fileif (fopen_s(&rgbOriFile, rgbOriName, "rb") == 0){cout << "Successfully opened " << rgbOriName << "." << endl;}else{cout << "Failed to open " << rgbOriName << "." << endl;exit(0);}if (fopen_s(&errorFile, errorName, "w") == 0){cout << "Successfully opened " << errorName << "." << endl;}else{cout << "Failed to open " << errorName << "." << endl;exit(0);}unsigned char* rgbOriBuf = new unsigned char[yuvSize * 2];fread(rgbOriBuf, sizeof(unsigned char), yuvSize * 2, rgbOriFile);// Output error data to csv filefprintf(errorFile, "Pixel,B Error,G Error,R Error\n");for (int i = 0; i < yuvSize * 2 / 3; i++){fprintf(errorFile, "%d,%d,%d,%d\n", i, (int)abs(rgbBuf[3 * i] - rgbOriBuf[3 * i]), (int)abs(rgbBuf[3 * i + 1] - rgbOriBuf[3 * i + 1]), (int)abs(rgbBuf[3 * i + 2] - rgbOriBuf[3 * i + 2]));}delete[]rgbOriBuf;fclose(rgbOriFile);fclose(errorFile);}

main.cpp

#include <iostream>#include "declarations.h"using namespace std;int main(int argc, char* argv[]){FILE* rgbOriFilePtr = NULL;// File pointer of the original RGB imageFILE* yuvFilePtr = NULL;// File pointer of YUV imageFILE* rgbRecFilePtr = NULL;// File pointer of the restored RGB fileconst char* rgbOriFileName = argv[1];// Original RGB image file nameconst char* yuvFileName = argv[2];// YUV image file nameconst char* rgbRecFileName = argv[3];// Restore RGB image file nameint width = 256;// image widthint height = 256;// image heightint rgbFileSize;// Total bytes of RGB imageint yuvFileSize;// Total bytes of YUV imageunsigned char* rgbOriBuffer = NULL;// Original RGB image bufferunsigned char* yBuffer = NULL;// Y component bufferunsigned char* uBuffer = NULL;// U component bufferunsigned char* vBuffer = NULL;// V component bufferunsigned char* rgbRecBuffer = NULL;// Restore RGB image buffer// open a fileif (fopen_s(&rgbOriFilePtr, rgbOriFileName, "rb") == 0){cout << "Successfully opened " << rgbOriFileName << "." << endl;}else{cout << "Failed to open " << rgbOriFileName << "." << endl;exit(0);}if (fopen_s(&yuvFilePtr, yuvFileName, "wb+") == 0){cout << "Successfully opened " << yuvFileName << "." << endl;}else{cout << "Failed to open " << yuvFileName << "." << endl;exit(0);}if (fopen_s(&rgbRecFilePtr, rgbRecFileName, "wb") == 0){cout << "Successfully opened " << rgbRecFileName << "." << endl;}else{cout << "Failed to open " << rgbRecFileName << "." << endl;exit(0);}// Calculate the total number of bytes of the original RGB imagefseek(rgbOriFilePtr, 0L, SEEK_END);rgbFileSize = ftell(rgbOriFilePtr);rewind(rgbOriFilePtr);cout << "The space that " << rgbOriFileName << " accounts for is " << rgbFileSize << " Bytes = " << rgbFileSize / 1024 << " kB." << endl;yuvFileSize = rgbFileSize / 2;// Create a bufferrgbOriBuffer = new unsigned char[rgbFileSize];yBuffer = new unsigned char[rgbFileSize / 3];uBuffer = new unsigned char[rgbFileSize / 12];// 4:2:0 formatvBuffer = new unsigned char[rgbFileSize / 12];rgbRecBuffer = new unsigned char[rgbFileSize];fread(rgbOriBuffer, sizeof(unsigned char), rgbFileSize, rgbOriFilePtr);// RGB image is read into the bufferrgb2yuv(yuvFilePtr, rgbFileSize, width, height, rgbOriBuffer, yBuffer, uBuffer, vBuffer);yuv2rgb(rgbRecFilePtr, yuvFileSize, width, height, yBuffer, uBuffer, vBuffer, rgbRecBuffer);errorData(yuvFileSize, rgbRecBuffer, argv);delete[]rgbOriBuffer;delete[]yBuffer;delete[]uBuffer;delete[]vBuffer;delete[]rgbRecBuffer;fclose(rgbOriFilePtr);fclose(yuvFilePtr);fclose(rgbRecFilePtr);}

Experimental results and error analysis

Conversion between RGB and YUV color space (2)

down.rgb

Conversion between RGB and YUV color space (3)

down_transformed.yuv

Conversion between RGB and YUV color space (4)

down_recoverd.rgb

The above three pictures are the original RGB image, the YUV image converted by RGB, and the RGB image restored by YUV. Comparing the first and third pictures, the difference is almost indistinguishable by the naked eye. In order to quantify the error, in the program, useerrorDataThe function calculates the errors of the three components of each pixel of the two RGB images and outputs them to a csv file.

Because it is inconvenient to analyze and visualize data in C++, considering the large amount of data, R is used for analysis.

Make boxplot and histogram separately in R:

errorData <- read.csv("errorData.csv")b.error <- errorData[, 2]g.error <- errorData[, 3]r.error <- errorData[, 4]boxplot(r.error, g.error, b.error, horizontal = TRUE, names = c("R Error", "G Error", "B Error"), col = c("coral2", "palegreen1", "skyblue1"))hist(r.error, freq = FALSE, xlab = "Pixel", ylab = "Frequency of R Error", col = "coral2")hist(g.error, freq = FALSE, xlab = "Pixel", ylab = "Frequency of G Error", col = "palegreen1")hist(b.error, freq = FALSE, xlab = "Pixel", ylab = "Frequency of B Error", col = "skyblue1")
Conversion between RGB and YUV color space (5)
Conversion between RGB and YUV color space (6)
Conversion between RGB and YUV color space (7)
Conversion between RGB and YUV color space (8)

The Empirical CDF of each component error can be obtained:

> ecdf.r.error <- ecdf(r.error)> ecdf.g.error <- ecdf(g.error)> ecdf.b.error <- ecdf(b.error)> ecdf.r.error(5)[1] 0.9351196> ecdf.g.error(5)[1] 0.9855804> ecdf.b.error(5)[1] 0.8774567

The chart shows that the conversion of this chromaticity space cannot be 100% accurate. The sources of error may include:

  • Since 3/4 of the chroma information is discarded when converting from 4:4:4 RGB image to 4:2:0 YUV image, it is impossible to restore the discarded chroma information when restoring to YUV file. of;
  • When deriving the color space conversion formula, the shift operation is used instead of the division operation, and there is rounding in the calculation process;
  • During the conversion from YUV to RGB, some data overflowed.

However, the pixel errors of 93.5%, 98.6%, and 87.8% of the R, G, and B components are less than or equal to 5, so the color space conversion error of this algorithm is not large; because the human eye is much more sensitive to chromaticity The sensitivity and error of brightness are also beyond the resolving power of the human eye.

Conversion between RGB and YUV color space (2024)
Top Articles
Latest Posts
Article information

Author: Rubie Ullrich

Last Updated:

Views: 6351

Rating: 4.1 / 5 (72 voted)

Reviews: 95% of readers found this page helpful

Author information

Name: Rubie Ullrich

Birthday: 1998-02-02

Address: 743 Stoltenberg Center, Genovevaville, NJ 59925-3119

Phone: +2202978377583

Job: Administration Engineer

Hobby: Surfing, Sailing, Listening to music, Web surfing, Kitesurfing, Geocaching, Backpacking

Introduction: My name is Rubie Ullrich, I am a enthusiastic, perfect, tender, vivacious, talented, famous, delightful person who loves writing and wants to share my knowledge and understanding with you.