【数据压缩】yuv2rgb

    xiaoxiao2021-03-25  108

    1、本实验涉及原理

    1YUVRGBRGB空间的相互转换          由电视原理可知,亮度和色差信号的构成如下:          Y0.2990R+0.5870G+0.1140B          R-Y0.7010R -0.5870G -0.1140B          B-Y-0.2990R -0.5870G+0.8860B 为了使色差信号的动态范围控制在0.5 0.5之间,需要进行归一化对色差信号引入压缩系数。归一化后的色差信号为:         U-0.1684R -0.3316G+0.5B         V0.5R -0.4187G -0.0813B

    自然,我们可以写出由YUV表示的RGB的表达式,我们可以自己计算,我是从网上查到的公式如下:

    (2)我们知道从RGB文件转换成YUV文件时,是分别写入y_bufferu_bufferv_buffer的,所以在YUVRGB文件时我沿袭了这个想法,读入的YUV文件就开辟了三块区域分别存放YUV数据流。而在4:2:0格式下,UV分量的大小都只有Y分量的1/4,而RGB每个都是一样大小,所以y_buffer给开width*heightu_bufferv_buffer都是开width*height/4。而RGB每块区域都是width*height,所以RGB文件给开width*height*3大小的缓存。

    需要注意的是,我们写yuv2rgb.cpp时,原先从RGB4:2:0YUV需要进行下采样,所以4:2:0YUVRGB需要进行上采样,将一个UV分量写给我设置的中间缓存区(主要是存上采样后的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文件如下:

    发生了什么错误呢!检查一遍,进入单步调试,查看数据,发现是忘记将CrCb减去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:20u1/4大小  vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);//4:20v1/4大小    /* get an output buffer for a frame */  rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);//RGB3倍的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++;   //一个数据给四个         }     }          5yuv指针指向     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的调整回0255             *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大小,相应要将调试中的工作目录进行相应的调整

    1garden

    原始的YUV文件如下

    YUV2RGBYUV2RGB

    2tennis

    原始的YUV文件如下:

    YUV2RGBRGB2YUV后:

    3mobile

    原始的YUV文件如下:

    YUV2RGBRGB2YUV后:

     

    5、结论

    我们这次实验主要是进行了对RGBYUV文件的转换,让我充分理解了文件读入和书写的操作,也理解了RGBYUV文件在存放时的不同形式。自己动手编程序的困难之处在于畏难情绪,写程序时还要特别需要学会调试与查错,有时候没遇到的错误可以稍微搜索一下,也许就能调整vs的设置而解决。

    
    转载请注明原文地址: https://ju.6miu.com/read-14677.html

    最新回复(0)