1、本实验涉及原理
(1)YUV与RGBRGB空间的相互转换 由电视原理可知,亮度和色差信号的构成如下: Y=0.2990R+0.5870G+0.1140B R-Y=0.7010R -0.5870G -0.1140B B-Y=-0.2990R -0.5870G+0.8860B 为了使色差信号的动态范围控制在0.5 0.5之间,需要进行归一化对色差信号引入压缩系数。归一化后的色差信号为: U=-0.1684R -0.3316G+0.5B V=0.5R -0.4187G -0.0813B
自然,我们可以写出由YUV表示的RGB的表达式,我们可以自己计算,我是从网上查到的公式如下:
(2)我们知道从RGB文件转换成YUV文件时,是分别写入y_buffer,u_buffer和v_buffer的,所以在YUV转RGB文件时我沿袭了这个想法,读入的YUV文件就开辟了三块区域分别存放YUV数据流。而在4:2:0格式下,UV分量的大小都只有Y分量的1/4,而RGB每个都是一样大小,所以y_buffer给开width*height,u_buffer和v_buffer都是开width*height/4。而RGB每块区域都是width*height,所以RGB文件给开width*height*3大小的缓存。
需要注意的是,我们写yuv2rgb.cpp时,原先从RGB转4:2:0的YUV需要进行下采样,所以4:2:0的YUV转RGB需要进行上采样,将一个U或V分量写给我设置的中间缓存区(主要是存上采样后的UV分量,大小是和Y区一样的width*height)的上下左右四个指针区域。
2、本实验的流程、结论及分析
上课时老师给了我们将RGB转成YUV文件的.h和.cpp文件,我将其中出现问题的强制类型转换修改后(将unsigned char改为short),出现了一个error,提示fatal error LNK 1123:转换到COFF期间失败:文件无效或损坏。心急如焚,上网搜索后获得解决方法,将清单工具-->输入和输出-->嵌入清单,后面改成否,如图:
然后运行,顺利得到了YUV图像如下:
然后我将这个YUV文件放入自己创建的YUV2RGB文件test文件夹中等待转换成RGB文件,程序跳出一个对话框如下
检查后发现是yuv2rgb.cpp中,不该将没开内存的指针free,将程序修改如下:
然后再放回老师的RGB2YUV项目中得到相应的YUV文件如下:
发生了什么错误呢!检查一遍,进入单步调试,查看数据,发现是忘记将Cr、Cb减去128,造成了数据的溢出和错误,修改程序,得到YUV文件如下:
其中那些红色的点是怎么造成的?因为得出的图像已经接近于初始图像,而这往往不是程序运行的问题,而应该是一些参数设置的小问题了,原因出在转成RGB文件时不在0-255间的数据会溢出,产生错误,所以我们加上中间变量和限制条件,使数据在0-255之间,运行便得到YUV文件:
3、本实验的关键代码及分析
main.cpp文件选取:
/* get the input buffers for a frame */从一帧中得到y\u\v分量 yBuf = (u_int8_t*)malloc(frameWidth * frameHeight); //分配y区的空间大小 uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);//4:2:0,u是1/4大小 vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);//4:2:0,v是1/4大小 /* get an output buffer for a frame */ rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);//RGB是3倍的size
if (rgbBuf == NULL ||yBuf == NULL || uBuf == NULL|| vBuf == NULL) { printf("no enought memory\n"); exit(1); }
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, yBuf,uBuf,vBuf, rgbBuf,flip)) //我将YUV2RGB函数改写了 { printf("error"); return 0; } fwrite(rgbBuf,1,frameWidth * frameHeight*3,rgbFile); }
printf("\n%u %ux%u video frames written\n", videoFramesWritten, frameWidth, frameHeight);
千万不要忘记将开的空间free哦!!
YUV2RGB.cpp文件选取:
(1)数组的存储,这样方便提取
static float YUVRGB[256], YUVRGB1402[256]; static float YUVRGB03441[256], YUVRGB07141[256]; static float YUVRGB11772[256];
(2)文件头改写 int YUV2RGB (int x_dim, int y_dim, void *y_in, void *u_in, void *v_in, void*rgb_out, int flip)
(3)定义的部分指针 unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv; //上采样时用到的 unsigned char *y_buffer, *u_buffer, *v_buffer; unsigned char *rgb_buffer;//创建的rgb数据区 unsigned char *u_buf,*v_buf;//创建的uv中间缓冲区 long pr,pg,pb;//中间变量
(4)上采样 // upsample UV for (i = 0; i < y_dim / 2; i++) { pu1 = u_buf + 2 * i * x_dim;//确定pu1的位置 pu2 = u_buf + (2 * i + 1) * x_dim; pv1 = v_buf + 2 * i * x_dim; pv2 = v_buf + (2 * i + 1) * x_dim; psu = u_buffer + i * x_dim / 2;//确定psu(就是之前读入的yuv文件的u文件的首指针) psv = v_buffer + i * x_dim / 2; for (j = 0; j < x_dim / 2; j++) { *pu1 = *psu; *pu2 = *psu; *pv1 = *psv; *pv2 = *psv; pu1++;pu2++; pv1++;pv2++; *pu1 = *psu; *pu2 = *psu; *pv1 = *psv; *pv2 = *psv; pu1++;pu2++; pv1++;pv2++; psu++;psv++; //一个数据给四个 } } (5)yuv指针指向 y = y_buffer;//是我们读入数据里的y区 u = u_buf;//是我们创建的缓冲区里的u区 v = v_buf;//是我们创建的缓冲区里的v区 (6) 转换 YUV 到 RGB if (!flip) { } else { for (i = 0; i < size; i++) { pr=(YUVRGB[*y]+YUVRGB1402[*v]); pg=(YUVRGB[*y]-YUVRGB03441[*u]-YUVRGB07141[*v]); pb=(YUVRGB[*y]+YUVRGB11772[*u]); //判断rgb是否发生溢出,溢出的话就将小于0及大于255的调整回0或255 *r=(pr>0?(pr>255? 255:(unsigned char)pr):0); *g =(pg>0 ? (pg>255 ? 255 : (unsigned char)pg) : 0); *b =(pb>0 ? (pb>255 ? 255 : (unsigned char)pb) : 0); y++; u++; v++; r+=3; g+=3; b+=3; } }
if (u_buf != NULL) free(u_buf); if (v_buf != NULL) free(v_buf);
(7)数组内部 void InitLookupTable() { int i;
for (i = 0; i < 256; i++) YUVRGB[i] = (float)i; for (i = 0; i < 256; i++) YUVRGB1402[i] = (float)1.402*(i-128); for (i = 0; i < 256; i++) YUVRGB03441[i] = (float)0.3441 *(i-128); for (i = 0; i < 256; i++) YUVRGB07141[i] = (float)0.7141 *(i-128); for (i = 0; i < 256; i++) YUVRGB11772[i] = (float)1.1772* (i-128); }
4、本实验的其他测试
从网上找的YUV文件资料,是352*240大小,相应要将调试中的工作目录进行相应的调整
(1)garden
原始的YUV文件如下
YUV2RGB再YUV2RGB后
(2)tennis
原始的YUV文件如下:
YUV2RGB再RGB2YUV后:
(3)mobile
原始的YUV文件如下:
YUV2RGB再RGB2YUV后:
5、结论
我们这次实验主要是进行了对RGB与YUV文件的转换,让我充分理解了文件读入和书写的操作,也理解了RGB与YUV文件在存放时的不同形式。自己动手编程序的困难之处在于畏难情绪,写程序时还要特别需要学会调试与查错,有时候没遇到的错误可以稍微搜索一下,也许就能调整vs的设置而解决。