文章目录
1.rgb空间与yuv空间对应关系:
根据电视原理相关知识可知RGB与的YUV对应关系为:
Y=0.299R +0.587G+0.114B
U=-0.1684R-0.3316G+0.5B
V=0.5R-0.4187G-0.0813B
为了使色差信号的动态范围控制在[-0.5, 0.5],需要进行量化前的归一化处理,需要引入数字色差信号的压缩系数(分别为0.564与0.713)。
2.量化电平的分配
参考《现代电视原理》“视频信号量化电平的分配”部分:
在进行8 bit量化时,需要在上下两端留出一定的余量,作为信号超越动态范围的保护带。具体地:
对于亮度信号,在256级的上端留出20级,下端留出16级作为余量,即Y的动态范围为16—235;
对于两个色差信号,在256级的上端留出15级,下端留出16级作为余量,即U、V的动态范围为16—240。
3.利用RGB转YUV的转换公式,整理得到:
R’=1.1686Y+1.16118V-224.8784
G’=1.1686Y-0.3961U-0.8274V+136.2314
B’=1.1686Y+2.0353U-278.8902
4.main函数的参数
main函数具有两个形参,int argc和char* argv[]。在涉及文件的操作中,使用命令行参数可以为编程提供一定的便利。如下图:
5.查找表的使用
使用查找表法,使用存储空间换运算时间。
(1)RGB转YUV
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;
for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;
for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;
for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;
for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;
for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;
for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i;
}
(1)YUV转RGB
void yuvLookupTable()
{
for (int i = 0; i < 256; i++)
{
yuv11686[i] = 1.1686 * i;
yuv16118[i] = 1.6118 * i;
yuv03961[i] = 0.3961 * i;
yuv08274[i] = 0.8274 * i;
yuv20353[i] = 2.0353 * i;
}
}
6.代码部分
(1)main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "rgb2yuv.h"
#include "yuv2rgb.h"
#include <iostream>
using namespace std;
#define u_int8_t unsigned __int8
#define u_int unsigned __int32
#define u_int32_t unsigned __int32
#define FALSE false
#define TRUE true
int main(int argc, char** argv)
{
/* variables controlable from command line */
u_int frameWidth = 352; /* --width=<uint> */
u_int frameHeight = 240; /* --height=<uint> */
bool flip = TRUE; /* --flip */
unsigned int i;
/* internal variables */
char* rgbFileName = NULL;
char* yuvFileName = NULL;
char* rgbFileName1 = NULL;
char* yuvFileName1 = NULL;
FILE* rgbFile = NULL;
FILE* yuvFile = NULL;
FILE* rgbFile1 = NULL;
FILE* yuvFile1 = NULL;
u_int8_t* rgbBuf = NULL;
u_int8_t* rgbBuf1 = NULL;
u_int8_t* yuvBuf = NULL;
u_int8_t* yBuf = NULL;
u_int8_t* uBuf = NULL;
u_int8_t* vBuf = NULL;
u_int32_t videoFramesWritten = 0;
/* begin process command line */
/* point to the specified file names */
rgbFileName = argv[1];
yuvFileName = argv[2];
rgbFileName1 = argv[6];
yuvFileName1 = argv[5];
cout << rgbFileName1 << endl;
cout << yuvFileName1 << endl;
frameWidth = atoi(argv[3]);
frameHeight = atoi(argv[4]);
/* open the RGB file */
if (fopen_s(&rgbFile, rgbFileName, "rb")==0)
{
printf("The input rgb file is %s\n", rgbFileName);
}
else
{
cout << rgbFileName<<endl;
printf("cannot find rgb file\n");
exit(0);
}
/* open the RAW file */
if (fopen_s(&yuvFile, yuvFileName, "wb") == 0)
{
printf("The output yuv file is %s\n", yuvFileName);
}
else
{
printf("cannot find yuv file\n");
exit(0);
}
if (fopen_s(&rgbFile1, rgbFileName1, "wb") == 0)
{
//printf("The input rgb file is %s\n", rgbFileName1);
}
else
{
cout << rgbFileName1 << endl;
printf("cannot find rgb file\n");
exit(0);
}
/* open the RAW file */
if (fopen_s(&yuvFile1, yuvFileName1, "rb") == 0)
{
//printf("The output yuv file is %s\n", yuvFileName1);
}
else
{
printf("cannot find yuv file\n");
exit(0);
}
/* get an input buffer for a frame */
rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
yuvBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3 / 2);
rgbBuf1 = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
/* get the output buffers for a frame */
yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
unsigned char* yBuffer = NULL; // Y分量缓冲区
unsigned char* uBuffer = NULL; // U分量缓冲区
unsigned char* vBuffer = NULL; // V分量缓冲区
unsigned char* rgbRecBuffer = NULL; // 复原RGB图像缓冲区
if (rgbBuf == NULL || yBuf == NULL || uBuf == NULL || vBuf == NULL)
{
printf("no enought memory\n");
exit(1);
}
while (fread(rgbBuf, 1, frameWidth * frameHeight * 3, rgbFile))
{
if(RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip))
{
printf("error");
return 0;
}
for (i = 0; i < frameWidth*frameHeight; i++)
{
if (yBuf[i] < 16) yBuf[i] = 16;
if (yBuf[i] > 235) yBuf[i] = 235;
}
for (i = 0; i < frameWidth*frameHeight/4; i++)
{
if (uBuf[i] < 16) uBuf[i] = 16;
if (uBuf[i] > 240) uBuf[i] = 240;
if (vBuf[i] < 16) vBuf[i] = 16;
if (vBuf[i] > 240) vBuf[i] = 240;
}
fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
printf("\r...%d", ++videoFramesWritten);
}
yBuffer = new unsigned char[frameWidth* frameHeight];
uBuffer = new unsigned char[frameWidth * frameHeight / 4]; // 4:2:0格式
vBuffer = new unsigned char[frameWidth * frameHeight / 4];
rgbRecBuffer = new unsigned char[frameWidth * frameHeight * 3];
fread(yBuffer,sizeof(unsigned char), frameWidth* frameHeight , yuvFile1);
fread(uBuffer, sizeof(unsigned char), frameWidth * frameHeight / 4, yuvFile1);
fread(vBuffer, sizeof(unsigned char), frameWidth * frameHeight / 4, yuvFile1);
yuv2rgb(rgbFile1, 256*256*3/2, frameWidth, frameHeight, yBuffer, uBuffer, vBuffer, rgbRecBuffer);
cout << "win" << endl;
delete[]yBuffer;
delete[]uBuffer;
delete[]vBuffer;
delete[]rgbRecBuffer;
printf("\n%u %ux%u video frames written\n",
videoFramesWritten, frameWidth, frameHeight);
/* cleanup */
fclose(rgbFile);
fclose(yuvFile);
fclose(rgbFile1);
fclose(yuvFile1);
return(0);
}
(2)rgb2yuv.h
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip);
void InitLookupTable();
(3)yuv2rgb.h
void yuv2rgb(FILE* rgbFile, int yuvSize, int w, int h, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf, unsigned char* rgbBuf);
(4)yuv2rgb.cpp
#include <iostream>
using namespace std;
double yuv11686[256], yuv16118[256];
double yuv03961[256], yuv08274[256];
double yuv20353[256];
void yuvLookupTable()
{
for (int i = 0; i < 256; i++)
{
yuv11686[i] = 1.1686 * i;
yuv16118[i] = 1.6118 * i;
yuv03961[i] = 0.3961 * i;
yuv08274[i] = 0.8274 * i;
yuv20353[i] = 2.0353 * 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]; // 还原成4:4:4的U分量缓冲区
unsigned char* vBuf444 = new unsigned char[yuvSize * 2 / 3]; // 还原成4:4:4的V分量缓冲区
// 4:2:0 to 4:4:4
for (int i = 0; i < h / 2; i++)
{
for (int j = 0; j < w / 2; j++)
{
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 RGB
for (int i = 0; i < w * h; i++)
{
int y = yBuf[i];
int u = uBuf444[i];
int v = vBuf444[i];
double r;
double g;
double b;
yuvLookupTable();
r = yuv11686[y] + yuv16118[v] - 224.9784;
if (r < 0)
r = 0;
if (r > 255)
r = 255;
g = yuv11686[y]-yuv03961[u]-yuv08274[v]+136.2314; // 还原的RGB图像的G分量
if (g < 0)
g = 0;
if (g > 255)
g = 255;
b = yuv11686[y]+yuv20353[u]-278.8902; // 还原的RGB图像的B分量
if (b < 0)
b = 0;
if (b > 255)
b = 255;
rgbBuf[3 * i + 2] = (unsigned char)r; // 还原的RGB图像的R分量
rgbBuf[3 * i + 1] = (unsigned char)g; // 还原的RGB图像的G分量
rgbBuf[3 * i] = (unsigned char)b; // 还原的RGB图像的B分量
}
delete[]uBuf444;
delete[]vBuf444;
cout << *(rgbBuf +1) << endl;
unsigned char* temp = new unsigned char[yuvSize * 2];
unsigned char* temp1 = new unsigned char[yuvSize * 2];
int i = 0, j = 256 * 256 * 3;
for(i = 0; j >= 0; i += 3, j -= 3)
{
*(temp+i) = *(rgbBuf+j-3);
*(temp+i+1) = *(rgbBuf+j - 2);
*(temp+i+2) = *(rgbBuf+j - 1);
}
for (i = 0; i < 256; i++)
{
for (j = 0; j < 256; j++)
{
*(temp1 + 3 * j+i*256*3) = *(temp + (255 - j) * 3 + i * 256 * 3);
*(temp1 + 3 * j+1 + i * 256 * 3) = *(temp + (255 - j) * 3+1 + i * 256 * 3);
*(temp1 + 3 * j+2 + i * 256 * 3) = *(temp + (255 - j) * 3+2 + i * 256 * 3);
}
}
fwrite(temp1, sizeof(unsigned char), yuvSize * 2, rgbFile);
}
(5)rgb2yuv.cpp
#include "stdlib.h"
#include "rgb2yuv.h"
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip)
{
static int init_done = 0;
long i, j, size;
unsigned char *r, *g, *b;
unsigned char *y, *u, *v;
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
unsigned char *y_buffer, *u_buffer, *v_buffer;
unsigned char *sub_u_buf, *sub_v_buf;
if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}
// check to see if x_dim and y_dim are divisible by 2
if ((x_dim % 2) || (y_dim % 2)) return 1;
size = x_dim * y_dim;
// allocate memory
y_buffer = (unsigned char *)y_out;
sub_u_buf = (unsigned char *)u_out;
sub_v_buf = (unsigned char *)v_out;
u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
if (!(u_buffer && v_buffer))
{
if (u_buffer) free(u_buffer);
if (v_buffer) free(v_buffer);
return 2;
}
b = (unsigned char *)bmp;
y = y_buffer;
u = u_buffer;
v = v_buffer;
// convert RGB to YUV
if (!flip) {
for (j = 0; j < y_dim; j ++)
{
y = y_buffer + (y_dim - j - 1) * x_dim;
u = u_buffer + (y_dim - j - 1) * x_dim;
v = v_buffer + (y_dim - j - 1) * x_dim;
for (i = 0; i < x_dim; i ++) {
g = b + 1;
r = b + 2;
*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);
*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 3;
y ++;
u ++;
v ++;
}
}
} else {
for (i = 0; i < size; i++)
{
g = b + 1;
r = b + 2;
*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);
*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 3;
y ++;
u ++;
v ++;
}
}
// subsample UV
for (j = 0; j < y_dim/2; j ++)
{
psu = sub_u_buf + j * x_dim / 2;
psv = sub_v_buf + j * x_dim / 2;
pu1 = u_buffer + 2 * j * x_dim;
pu2 = u_buffer + (2 * j + 1) * x_dim;
pv1 = v_buffer + 2 * j * x_dim;
pv2 = v_buffer + (2 * j + 1) * x_dim;
for (i = 0; i < x_dim/2; i ++)
{
*psu = (*pu1 + *(pu1+1) + *pu2 + *(pu2+1)) / 4;
*psv = (*pv1 + *(pv1+1) + *pv2 + *(pv2+1)) / 4;
psu ++;
psv ++;
pu1 += 2;
pu2 += 2;
pv1 += 2;
pv2 += 2;
}
}
free(u_buffer);
free(v_buffer);
return 0;
}
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;
for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;
for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;
for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;
for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;
for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;
for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i;
}
7.实验结果分析
(1)rgb转yuv
原图:
转换后:
(2)yuv转rgb
原图:
转换后:
8.误差分析
由yuv420图像转化来的rgb图像与原rgb图像之间是存在误差的,因为yuv420的uv分量只有原来的1/4,如果在一个色度变化慢的区域uv分量只取1/4对整体不太有影响,但是如果区域内色度变化剧烈,那么区域内的色度信息就会有很大误差。