一、实验原理
1.本次实验是实现YUV格式转换为RGB格式,这里的YUV其实是指数字高清的YCbCr。
根据亮度和色差计算公式,可以得到:
2.为使色差信号的动态范围控制在-0.5~+0.5之间,要对色差信号进行归一化处理,得到UV的计算公式:
由上式得到的UV范围在-128~+127之间,为避免负数,应在U、V之后各加128。
3.YUV在存储时,为了防止信号变得造成过载,在对分量信号进行8比特均匀量化时,Y分量256级上端留20级,下端留16级作为信号超越动态范围的保护带,UV分量256级上端留15级,下端留16级作为信号超越动态范围
的保护带。
4.这里YUV采用的是420格式,既色度信号是亮度信号取样频率的四分之一。
5.得到YUV转换RUV的公式:
二、实验流程分析
a.首先读取YUV文件,新建RGB文件
b.开辟RGB内存数组和YUV数组。由于RGB文件是按照BGRBGRBGR……的顺序存储一帧的,所以RBG开辟一个3倍像素数的数组,而YUV文件是YYYY……U……V……存储文件的,可以开辟一个像素数大小的数组存放Y分量和两个四分之一像素数大小的数组存放U和V分量。
c.读取每一帧的YUV值存在YUV三个缓存数组中
d.因为YUV有动态范围,先判断Y和UV是否超出要求的范围。
e.对YUV进行上采样,恢复出444格式的YUV。
f.按照转换公式,将每一个像素分别转换为RGB格式存在RGB缓存数组中。
e.重复c操作完成一帧的计算,再将RGB的值写入RGB文件。
三、关键代码及分析
1.本次实验工程文件包括两个源文件和一个头文件:
2.main.cpp是主文件:
a.通过工作参数设置目标文件和文件像素格式:
yuvFileName = argv[1];
rgbFileName = argv[2];
frameWidth = atoi(argv[3]);
frameHeight = atoi(argv[4]);
/* get an input buffer 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);
/* get the output buffers for a frame */
rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
C.读取YUV文件并判断是否超出YUV的动态范围,使用WHILE 函数,可以重复帧操作:
while (fread(yBuf, 1, frameWidth * frameHeight, yuvFile) && fread(uBuf, 1, frameWidth * frameHeight /4, yuvFile)
&& fread(vBuf, 1, frameWidth * frameHeight /4, yuvFile))
{
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;
}
YUV2RGB(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf)
e.将RGB数据存储进RGB文件:
fwrite(rgbBuf, 1, frameWidth * frameHeight*3, rgbFile);
a.对YUV进行上采用,恢复444格式:
//上采样
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;
pv1 = v_buffer + 2 * j*x_dim;
pu2 = u_buffer + (2 * j + 1)*x_dim;
pv2 = v_buffer + (2 * j + 1)*x_dim;
for (i = 0; i < x_dim / 2; i++)
{
*pu1 = *psu;
*(pu1 + 1) = *psu;
*pu2 = *psu;
*(pu2 + 1) = *psu;
*pv1 = *psv;
*(pv1 + 1) = *psv;
*pv2 = *psv;
*(pv2 + 1) = *psv;
psu++;
psv++;
pu1 += 2;
pu2 += 2;
pv1 += 2;
pv2 += 2;
}
}
for (i = 0; i < size ; i++)
{
aa = (int)((*y) + RGBYUV1779[*u]+0.5 );//b
bb = (int)((*y) - RGBYUV03455[*u] - RGBYUV07169[*v]+0.5);//g
cc = (int)((*y) + RGBYUV14075[*v]+0.5 );//r
//char a = *(b + 1); char c = *(b + 2);
if (aa>255)
aa = 255;
if (aa<0)
aa = 0;
if (bb>255)
bb = 255;
if (bb<0)
bb = 0;
if (cc>255)
cc = 255;
if (cc<0)
cc = 0;
*(b + 0) = (unsigned char) aa;
*(b + 1) = (unsigned char) bb;
*(b + 2) = (unsigned char) cc;
b += 3;
y++;
u++;
v++;
}
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV14075[i] = (float)1.4075 * (i-128);
for (i = 0; i < 256; i++) RGBYUV03455[i] = (float)0.3455 * (i-128);
for (i = 0; i < 256; i++) RGBYUV1779[i] = (float)1.779 *( i-128);
for (i = 0; i < 256; i++) RGBYUV07169[i] = (float)0.7169 * (i-128);
}
if (u_buffer)
free(u_buffer);
if (v_buffer)
free(v_buffer);
int YUV2RGB(int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out);
void InitLookupTable();
第一组例子
左图是RGB文件转换为YUV文件查看后,右图是将YUV文件转为RGB,再转为YUV查看:
以下三组例子
左图是YUV420素材,右图是将YUV文件转换为RGB,再转换为YUV的文件:
转换之后的视频与原文件相同,说明实验正确。
五、结论
通过本次实验,熟悉了RGB、YUV420、YUV444的存储格式,熟悉了指针、数组以及文件的操作方式。在实验中应注意正确设置工作路径和实验参数。