一、基本原理
1、rgb转yuv公式
Y=0.2990*R+0.5870*G+0.1140*B
R-Y=0.7010*R-0.5870*G-0.1140*B
B-Y=-0.2990*R-0.5870*G+0.8860*B
为了使色差信号的动态范围控制在0.5之间,需要进行归一化。
U= 0.492*(B-Y)= -0.147*R-0.289*G+0.436*B
V= 0.877*(R-Y)= 0.615*R-0.515*G-0.100*B
色差信号经过归一化处理后,动态范围为-0.5-0.5,让色差零电平对应码电平128,色差信号总共占225个量化级。在256级上端留15级,下端留16级作为信号超越动态范围的保护带。
U=-0.1684R-0.3316G+0.5B
V=0.5R-0.4187G-0.0813B
2、yuv转rgb公式
R = Y + 1.140*V G = Y - 0.394*U - 0.581*V B = Y + 2.032*U 经过归一化处理,使其动态范围控制在-0.5—0.5之间,并让零电平对应码电平128。 R = Y + 1.4075 *(V-128) G = Y – 0.3455 *(U –128) – 0.7169 *(V –128) B = Y + 1.779 *(U – 128)
3、rgb与yuv存储格式
(1)rgb存储格式
RGB格式的图像存储的顺序,是以B、G、R的顺序进行存储的。(4:4:4)
BGRBGR
(2)yuv存储格式
yuv格式的图像每四个Y共用一组UV分量,
色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半。(4:2:0)
Y Y Y YYYYY V U
二、实验流程分析
1、rgb2yuv
(1)定义指针,确定类型并开辟空间
(2)再读入rgb文件,根据公式计算出yuv
(3)对uv分量进行下采样
(4)写入yuv文件
2、yuv2rgb
(1)定义指针,确定类型并开辟空间
(2)打开yuv文件
(3)对uv分量进行上采样
(4)根据公式计算出rgb
(5)写入rgb文件
三、关键代码及其分析(yuv2rgb)
1、定义指针并开辟空间
u_int8_t* rgbBuf = NULL;
u_int8_t* yBuf = NULL;
u_int8_t* uBuf = NULL;
u_int8_t* vBuf = NULL;
/* get an input buffer for a frame */
yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);//yuv文件中v分量为v分量的1/4
vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);//yuv文件中u分量为y分量的1/4
/* get the output buffers for a frame */
rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);//rgb文件由rgb三个分量所构成,所以其存储大小应为一帧图像大小的三倍
2、打开yuv文件
char* rgbFileName = NULL;
char* yuvFileName = NULL;
FILE* rgbFile = NULL;
FILE* yuvFile = NULL;
/* open the YUV file */
yuvFile = fopen(yuvFileName, "rb");
if (yuvFile == NULL)
{
printf("cannot find yuv file\n");
exit(1);
}
else
{
printf("The input yuv file is %s\n", yuvFileName);
}
/* open the RAW file */
rgbFile = fopen(rgbFileName, "wb");
if (rgbFile == NULL)
{
printf("cannot find rgb file\n");
exit(1);
}
else
{
printf("The output rgb file is %s\n", rgbFileName);
}
3、YUV2RGB
(1)函数变量定义
int YUV2RGB(int x_dim, int y_dim, void *rgb, void *yr, void *ur, void *vr,int flip) 函数中指针的定义
unsigned char *r, *g, *b;//分别指向输出的rgb文件中的r,g,b
unsigned char *y, *u, *v;//分别指向输入的yuv文件中的y,u,v
unsigned char *y_buffer, *u_buffer, *v_buffer;//分别寄存yuv文件中的y,u,v
unsigned char *sub_u_buf, *sub_v_buf;//分别寄存上采样后的u,v
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;//用于u,v的上采样
y_buffer = (unsigned char *)yr;
sub_u_buf = (unsigned char *)ur;
sub_v_buf = (unsigned char *)vr;
u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
b= (unsigned char *)rgb;
y = y_buffer;
u = u_buffer;
v = v_buffer;
(2)定义查找表,方便计算
static float YUVRGB14075[256];
static float YUVRGB03455[256], YUVRGB07169[256];
static float YUVRGB17790[256];
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) YUVRGB14075[i] = (float)1.4075 *(i - 128);
for (i = 0; i < 256; i++) YUVRGB03455[i] = (float)0.3455* (i - 128);
for (i = 0; i < 256; i++) YUVRGB07169[i] = (float)0.7169 * (i - 128);
for (i = 0; i < 256; i++) YUVRGB17790[i] = (float)1.7790 *(i - 128);
}
(3)对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++)
{
*pu1 = *psu;
*(pu1 + 1) = *pu1;
*pu2 = *psu;
*(pu2 + 1) = *pu2;
*pv1 = *psv;
*(pv1 + 1) = *pv1;
*pv2 = *psv;
*(pv2 + 1) = *pv2;
psu++;
psv++;
pu1 += 2;
pu2 += 2;
pv1 += 2;
pv2 += 2;
}
}(4)根据公式计算出rgb
for (i = 0; i < size; i++)
{
float br;
float gr;
float rr;
//用来判断是否溢出,小于0则为0,大于255则为255
g = b + 1;
r = b + 2;
br = *y + YUVRGB17790[*u];
if (br < 0) br = 0;
if (br > 255) br = 255;
*b = (unsigned char)br;
gr = *y - YUVRGB03455[*u] - YUVRGB07169[*v];
if (gr < 0) gr = 0;
if (gr > 255) gr = 255;
*g = (unsigned char)gr;
rr = *y + YUVRGB14075[*v];
if (rr < 0) rr = 0;
if (rr > 255) rr = 255;
*r = (unsigned char)rr;
b += 3;
y++;
u++;
v++;
}
4、写入rgb文件
while (fread(yBuf, 1, frameWidth * frameHeight, yuvFile) &&fread(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile) &&
fread(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile))
{
if (YUV2RGB(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf,flip))
{
printf("error");
return 0;
}
for (i = 0; i < frameWidth*frameHeight*3; i++)
{
if (rgbBuf[i] < 16) rgbBuf[i] = 16;
if (rgbBuf[i] > 235) rgbBuf[i] = 235;
}
fwrite(rgbBuf, 1, frameWidth * frameHeight * 3, rgbFile);
printf("\r...%d", ++videoFramesWritten);
}
四、实验结果及分析
1、实验结果
(1)rgb2yuv
(2)yuv2rgb
由以上几幅图观察得知:yuv转成rgb时,会有色差出现,红色会偏蓝,蓝色会偏红。
2、实验中出现的问题
(1)rgb文件中出现红点,yuv文件中出现蓝点(此图中还有色差错误下一标题解释)
原因:在转换RGB是没有进行数值判断,导致溢出。
原代码: 改正后:
(2)生成的yuv文件与原yuv文件存在色差,生成的yuv文件偏红
原因:公式写错了,rgb文件格式是以bgr的顺序存放的,而本人在编写代码时没有认真查看,把本应该是r的公式写到了b的位置,把b的公式写到了r处,导致产生了色差。
在这里温馨提示:编写代码时一定要认真仔细,不要犯这种低级错误,给后来查错添不必要的麻烦。
原代码: 改正后:
五、总结
通过本次实验,我练习了rgb与yuv图片格式之间的转换,熟悉了VS的编程环境以及调试程序的过程;同时了解了yuv和rgb图像的格式和存储原理。
转载请注明原文地址: https://ju.6miu.com/read-36535.html