experiment one:
Write RGB to YUV program, focus on function definition, initialization and calling of some lookup tables, buffer allocation. Convert the obtained RGB file to YUV file and watch it with YUV Viewer player to verify whether it is correct.
Write a program to convert YUV to RGB. Use this program to convert the given experimental data into RGB files. And with the original
RGB files are compared, and if there are errors, analyze where the errors come from.
Article Directory
- (1) Conversion formula and file storage format of YUV and RGB
- (Two) the command line parameters of the main function
- 2.1 Representation
- 2.2 How to use
- (3) Preliminary realization of color space conversion (no sampling) code
- main.cpp
- rgb2yuv.h
- rgb2yuv.cpp
- yuv2rgb.h
- yuv2rgb.cpp
- Experimental results
- (4) Analysis of the causes of experimental errors and code modification
- Error correction
- Experimental results
- (5) Optimize the code (using the lookup table method)
- main.cpp
- yuvrgb.h
- yuvrgb.cpp
- Experimental results
R=(298×Y+411×V−57344)>>8G=(298×Y−101×U−211×V+34739)>>8B=(298×Y+519×U−71117)>>8Y=(66×R+129×G+25×B)>>8+16U=(−38×R−74×G+112×B)>>8+128V=(112×R−94×G−18×B)>>8+128
The above formula is already quantified.
Check out the information,RGBThe file storage format isBGRBGRBGR⋯,YUVThe file storage format is first save allY, Save allUAnd finally save allV。
2.1 Representation
One programmain()The function can contain two parameters:
- The first parameter isintTypes of;
- The second parameter is a string array;
Usually, the first parameter is namedargc, The second parameter isargv. Since the declaration of the string array in the function header can have two forms, somain()There are also two ways to write functions.
-
main()functionWriting one:
int main(int argc, char** argv){ return 0;}
-
main()functionWriting two:
int main(int argc, char* argv[]){ return 0;}
2.2 How to use
-
The meaning of the parameters:
int argc: Represents the number of strings.argc = 1 + the number of strings entered by the user, The value of argc is calculated automatically by the operating system, and the programmer does not need to assign it.
char argv[]*: It stores multiple strings, the form of the string is as follows:
argv[0] = the name of the executable file. For example, change.exe. (This string does not require user input, and is the same as argc, which can be automatically generated by the operating system.
argv[1] = string 1
argv[2] = string 2
argv[3] = string 3
⋮
-
How to input parameters in programming mode?
The platform used isVisualStudio2019, The file to be used isdown.rgb, The file to be generated isup.yuv,cho.rgb,The steps of parameter input are shown in the following figure:
1. Open the properties window of the upper taskbar debugging interface | |
---|---|
2. Select debug in configuration properties | |
3. Modify the command parameters as required |
Based on the above knowledge, the preliminary realization of color space conversion can be easily carried out. Header filergb2yuv.h,yuv2rgb.hAnd source filesmain.cpp,rgb2yuv.cpp,yuv2rgb.cppcomposition.
Solution Explorer is shown in the figure below:
Experiment codeas follows:
main.cpp
#include <iostream>#include <cstdio>#include <fstream>#include "rgb2yuv.h"#include "yuv2rgb.h"using namespace std;#define size 196608#define usize 65536#define vsize 131072using namespace std;int main(int argc, char** argv){ifstream infile(argv[1],ios::binary);ofstream outYUV(argv[2], ios::binary);ofstream outRGB(argv[3], ios::binary);if (!infile) { cout << "error to open file1!" << endl; }if (!outYUV) { cout << "error to open file2" << endl; }if (!outRGB) { cout << "error to open file3" << endl; }unsigned char* in = new unsigned char[size];unsigned char* YUV = new unsigned char[size];unsigned char* RGB = new unsigned char[size];infile.read((char*)in, size);rgb2yuv(in,YUV,size, usize, vsize);//First conversionyuv2rgb(YUV, RGB, usize, vsize);//The second conversion/*for (int i = 0; i < size; i++){if (abs(in[i] - RGB[i]) > 5)cout << "i=" << i << " in[" << i << "]=" << int(in[i]) << " RGB[" << i << "]=" << int(RGB[i]) << endl;}*/outYUV.write((char*)YUV, size);outRGB.write((char*)RGB, size);delete in;delete YUV;delete RGB;infile.close();outYUV.close();outRGB.close();return 0;}
rgb2yuv.h
#pragma oncevoid rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size,int usize,int vsize);
rgb2yuv.cpp
void rgb2yuv(unsigned char* rgb, unsigned char* yuv,int size,int usize,int vsize){unsigned char r, g, b, y, u, v;int j = 0;for (int i = 0;i < size;){b = *(rgb + i);g = *(rgb + i + 1);r = *(rgb + i + 2);y = ((66 * r + 129 * g + 25 * b) >> 8) + 16;u = ((-38 * r - 74 * g + 112 * b) >> 8) + 128;v = ((112 * r - 94 * g - 18 * b) >> 8) + 128;*(yuv + j) = y;*(yuv + j + usize) = u;*(yuv + j + vsize) = v;i = i + 3;//Each rgb is 1 groupj++;}}
yuv2rgb.h
#pragma oncevoid yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize);
yuv2rgb.cpp
#pragma once#include "yuv2rgb.h"#include <iostream>using namespace std;void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize){unsigned char r, g, b, y, u, v;int j = 0;for (int i = 0; i < usize; i++){y = *(yuv + i);u = *(yuv + i + usize);v = *(yuv + i + vsize);r = (298 * y + 411 * v - 57344) >> 8;g = (298 * y - 101 * u - 211 * v + 34739) >> 8;b = (298 * y + 519 * u - 71117) >> 8;*(rgb + j) = b;*(rgb + j + 1) = g;*(rgb + j + 2) = r;j = j + 3;}}
Experimental results
down.rgb | up.yuv | cho.rgb |
---|---|---|
among them,down.rgbwithcho.rgbuseYUVviewerPlusThe way to open is:
The opened image is an inverted image (due tobmpThe image format is stored backwards, so.rgbImage usebmpIt will fall when the mode is opened). The pictures in the above table have been rotated using WeChat for easy identification, butYUVFiles andRGBThere is still mirror flipping between files, but it does not affect viewing and comparison.
up.yuvuseYUVviewerPlusThe way to open is:
It can be seen from the comparison chart of the three images,RGBtoYUV'S experiment was successfully completed, andYUVtoRGBThere is a problem with the experiment, which is transferred outcho.rgbThere are more red noise in the image.
Error correction
Inferred available, in progressYUVtoRGBWhen you getRGBThree figures may exceedunsignedcharThe range that the type can represent, that is, possible<0or>255。
So it’s necessary toyuv2rgb.cppThe file is appropriately revised,>255Values are direct=255,<0Values are direct=0。
After modificationyuv2rgb.cppIs as follows:
#pragma once#include "yuv2rgb.h"#include <iostream>using namespace std;void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize){int r, g, b, y, u, v;int j = 0;for (int i = 0; i < usize; i++){y = int(*(yuv + i));u = int(*(yuv + i + usize));v = int(*(yuv + i + vsize));r = (298 * y + 411 * v - 57344) >> 8;if (r > 255) { r = 255; }if (r < 0) { r = 0; }g = (298 * y - 101 * u - 211 * v + 34739) >> 8;if (g > 255) { g = 255; }if (g < 0) { g = 0; }b = (298 * y + 519 * u - 71117) >> 8;if (b > 255) { b = 255; }if (b < 0) { b = 0; }*(rgb + j) = unsigned char(b);*(rgb + j + 1) = unsigned char(g);*(rgb + j + 2) = unsigned char(r);j = j + 3;}}
Experimental results
down.rgb | up.yuv | cho.rgb |
---|---|---|
So far, almost doneRGBtoYUVwithYUVtoRGBTwo experiments.
Using a lookup table to optimize the code. Header fileyuvrgb.hAnd source filesmain.cpp,yuvrgb.cppcomposition.
Solution Explorer is shown in the figure below:
main.cpp
#include <iostream>#include <cstdio>#include <fstream>#include "yuvrgb.h"using namespace std;#define size 196608#define usize 65536#define vsize 131072#define height 256#define weight 256//Lookup table initializationint* RGBYUV298 = new int[256];int* RGBYUV411 = new int[256];int* RGBYUV101 = new int[256];int* RGBYUV211 = new int[256];int* RGBYUV519 = new int[256];int* RGBYUV66 = new int[256];int* RGBYUV129 = new int[256];int* RGBYUV25 = new int[256];int* RGBYUV38 = new int[256];int* RGBYUV74 = new int[256];int* RGBYUV112 = new int[256];int* RGBYUV94 = new int[256];int* RGBYUV18 = new int[256];int main(int argc, char** argv){initLookupTable();ifstream infile(argv[1],ios::binary);ofstream outYUV(argv[2], ios::binary);ofstream outRGB(argv[3], ios::binary);if (!infile) { cout << "error to open file1!" << endl; }if (!outYUV) { cout << "error to open file2" << endl; }if (!outRGB) { cout << "error to open file3" << endl; }unsigned char* infi = new unsigned char[size];unsigned char* YUVfi = new unsigned char[size];unsigned char* RGBfi = new unsigned char[size];infile.read((char*)infi, size);rgb2yuv(infi, YUVfi, size, usize, vsize);yuv2rgb(YUVfi, RGBfi, usize, vsize);outYUV.write((char*)YUVfi, size);outRGB.write((char*)RGBfi, size);fileend(infi,YUVfi,RGBfi);infile.close();outYUV.close();outRGB.close();return 0;}
yuvrgb.h
#pragma oncevoid yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize);void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size, int usize, int vsize);void initLookupTable();void fileend(unsigned char* infi, unsigned char* YUVfi, unsigned char* RGBfi);
yuvrgb.cpp
#pragma once#include "yuvrgb.h"#include <iostream>using namespace std;extern int* RGBYUV298;extern int* RGBYUV411;extern int* RGBYUV101;extern int* RGBYUV211;extern int* RGBYUV519;extern int* RGBYUV66 ;extern int* RGBYUV129;extern int* RGBYUV25 ;extern int* RGBYUV38 ;extern int* RGBYUV74 ;extern int* RGBYUV112;extern int* RGBYUV94 ;extern int* RGBYUV18 ;void initLookupTable(){for (int i = 0; i < 256; i++){RGBYUV298[i] = 298 * i;RGBYUV411[i] = 411 * i;RGBYUV101[i] = 101 * i;RGBYUV211[i] = 211 * i;RGBYUV519[i] = 519 * i;RGBYUV66[i] = 66 * i;RGBYUV129[i] = 129 * i;RGBYUV25[i] = 25 * i;RGBYUV38[i] = 38 * i;RGBYUV74[i] = 74 * i;RGBYUV112[i] = 112 * i;RGBYUV94[i] = 94 * i;RGBYUV18[i] = 18 * i;}}void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize){int r, g, b, y, u, v;int j = 0;for (int i = 0; i < usize; i++){y = int(*(yuv + i));u = int(*(yuv + i + usize));v = int(*(yuv + i + vsize));/*r = (298 * y + 411 * v - 57344) >> 8;*/r = (RGBYUV298[y]+ RGBYUV411[v]-57344)>>8;if (r > 255) { r = 255; }if (r < 0) { r = 0; }/*g = (298 * y - 101 * u - 211 * v + 34739) >> 8;*/g = (RGBYUV298[y] - RGBYUV101[u] - RGBYUV211[v] + 34739) >> 8;if (g > 255) { g = 255; }if (g < 0) { g = 0; }/*b = (298 * y + 519 * u - 71117) >> 8;*/b = (RGBYUV298[y] + RGBYUV519[u] - 71117) >> 8;if (b > 255) { b = 255; }if (b < 0) { b = 0; }*(rgb + j) = unsigned char(b);*(rgb + j + 1) = unsigned char(g);*(rgb + j + 2) = unsigned char(r);j = j + 3;}}void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size, int usize, int vsize){int r, g, b, y, u, v;int j = 0;for (int i = 0; i < size;){b = int(*(rgb + i));g = int(*(rgb + i + 1));r = int(*(rgb + i + 2));/*y = ((66 * r + 129 * g + 25 * b) >> 8) + 16;*/y = ((RGBYUV66[r] + RGBYUV129[g] + RGBYUV25[b]) >> 8) + 16;/*u = ((-38 * r - 74 * g + 112 * b) >> 8) + 128;*/u = ((-RGBYUV38[r] - RGBYUV74[g] + RGBYUV112[b]) >> 8) + 128;/*v = ((112 * r - 94 * g - 18 * b) >> 8) + 128;*/v = ((RGBYUV112[r] - RGBYUV94[g] - RGBYUV18[b]) >> 8) + 128;/*if ((y > 255) || (u > 255) || (v > 255) || (y < 0) || (u < 0) || (v < 0)){cout << "y=" << y << "u=" << u << "v=" << v << endl;}*/*(yuv + j) = unsigned char(y);*(yuv + j + usize) = unsigned char(u);*(yuv + j + vsize) = unsigned char(v);i = i + 3;//Each rgb is 1 groupj++;}}void fileend(unsigned char* infi, unsigned char* YUVfi, unsigned char* RGBfi){delete infi;delete YUVfi;delete RGBfi;deleteRGBYUV298;deleteRGBYUV411;deleteRGBYUV101;deleteRGBYUV211;deleteRGBYUV519;deleteRGBYUV66;deleteRGBYUV129;deleteRGBYUV25;deleteRGBYUV38;deleteRGBYUV74;deleteRGBYUV112;deleteRGBYUV94;deleteRGBYUV18;}
Experimental results
down.rgb | up.yuv | cho.rgb |
---|---|---|
So far, 4:4:4 is completedRGBFile with 4:4:4YUVConversion between files.