【数据压缩】实验一 YUV转RGB

来源:互联网 发布:淘宝网上如何退货 编辑:程序博客网 时间:2024/06/05 05:24

一.实验原理

YUV转RGB的转换公式

R=Y+1.14075(V-128)

G=Y-0.7169(V-128)0.3455(U-128)

B=Y+1.779(U-128)

(公式中-128是由于调整动态幅度)

YUV4:2:0

    亮度信号Y和色度信号U、V是分离的。每一帧Y在一起存放,之后是U,最后是V。

YUV4:2:0指的是色差信号U和V在水平方向和垂直方向上的取样点数均为亮度信号的

一半,U和V的取样频率为Y取样频率的1/4。所以一帧图像的大小为(帧宽*帧高*(1+1/4+1/4))。




RGB格式

   采用这种编码方法,每个像素R,G,B的强度三个变量来表示。并按BRG的顺序进行存储。

所以一帧图像的大小为(帧宽*帧高*3)。

下采样(RGB转YUV)示意图

(用语言描述过于繁琐所以画图示意)


上采样(YUV转RGB)示意图


二.实验流程分析

1.利用命令行参数(argc,argv)读入所需要的信息,如待转换的文件名(argv[1]),输出的文件名(argv[2])以及图像的宽(argv[3])和高(argv[4]),(argv[0]用于调试不用赋值),工作目录中输入待转换文件的路径,命令参数写  <待转换文件名>   <输出文件名> <宽> < 高>;


2.设置初始化参数:

(1)用FILE *来设置指向输入输出文件的指针

(2)int定义宽高

(3)unsigned char*来定义rgbBuf,yBuf ,uBuf ,vBuf  其中用unsigned是为使值从0-255,并用malloc函数开辟空间(注意内存是顺序的)

3.用fopen打开两个文件并判断是否为空,若为空退出。

4.用fread读入yuv文件,y,u,v分别开buffer,用&&连接,注意u和v的buffer大小为frameWidth * frameHeight/4,为y的1/4。

5.利用实验原理,yuv与rgb转换公式编写函数实现转换,采用查表法,利用循环提前存入数组的方式实现。

6.用fwrite将rgbBuf中的数据导出到导出文件。

7.判断文件及buffer是否存在,若存在关闭文件,释放空间。

三.代码分析

(分析已以注释方式体现)

1.main.cpp

#include <stdio.h>#include <stdlib.h>#include <malloc.h>#include "yuv2rgb.h"#define u_int8_tunsigned __int8#define u_intunsigned __int32#define u_int32_tunsigned __int32#define FALSEfalse#define TRUEtrueint main(int argc, char** argv){u_int frameWidth = 352;u_int frameHeight = 240;bool flip = TRUE;unsigned int i;char* rgbFileName = NULL;char* yuvFileName = NULL;FILE* rgbFile = NULL;FILE* yuvFile = NULL;u_int8_t* rgbBuf = NULL;u_int8_t* yBuf = NULL;u_int8_t* uBuf = NULL;u_int8_t* vBuf = NULL;u_int32_t videoFramesWritten = 0;yuvFileName = argv[1];rgbFileName = argv[2];frameWidth = atoi(argv[3]);//字符串到整型frameHeight = atoi(argv[4]);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);}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);}rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);//由于采用4:2:0格式vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);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)){printf("error");return 0;}fwrite(rgbBuf, 1, frameWidth * frameHeight*3, rgbFile);//写入rgbFileprintf("\r...%d", ++videoFramesWritten);}printf("\n%u %ux%u video frames written\n", videoFramesWritten, frameWidth, frameHeight);/* cleanup */if (uBuf) free(uBuf);//释放空间if (vBuf) free(vBuf);if (yBuf) free(yBuf);if (rgbBuf) free(rgbBuf);if (rgbFile) fclose(rgbFile);//关闭文件        if (yuvFile) fclose(yuvFile);getchar();//让程序调试运行结束后等待编程者按下键盘才返回编辑界面return(0);}

2.yuv2rgb.cpp

#include "stdlib.h"#include "yuv2rgb.h"static float YUVRGB14075[256];static float YUVRGB03455[256],YUVRGB07169[256];static float YUVRGB1779[256];//采用查表法,将公式中所需要的用到的值先用循环算出存入数组int YUV2RGB(int x_dim, int y_dim, void *y_in,void *u_in,void *v_in, void *rgb_out, int flip){static int init_done = 0;long i, j, size;double rtemp,gtemp,btemp;unsigned char *r, *g, *b;unsigned char *y, *u, *v;unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;//在进行将u,v扩充的过程中用到的变量,psu用于指向没扩张之前的空间,pu1和pu2指向扩张后的空间unsigned char *y_buffer, *u_buffer, *v_buffer;//为扩张后的y,u,v空间unsigned char *sub_u_buf, *sub_v_buf;//为扩张前的u,v空间if (init_done == 0){InitLookupTable();init_done = 1;}if ((x_dim % 2) || (y_dim % 2)) return 1;size = x_dim * y_dim;y_buffer = (unsigned char *)y_in;sub_u_buf = (unsigned char *)u_in;sub_v_buf = (unsigned char *)v_in;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 *)rgb_out;y = y_buffer;u = u_buffer;v = v_buffer;// upsample UVfor (j = 0; j < y_dim/2; j ++){psu = sub_u_buf + j * x_dim / 2;//指向第j行的首地址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)=*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;}}if (flip)for (i = 0; i < size; i++){g = b + 1;//以b g r的形式存储r = b + 2;rtemp = (  *y + YUVRGB14075[*v]);//利用转换公式gtemp = (  *y - YUVRGB03455[*u] -YUVRGB07169[*v]);btemp = (  *y + YUVRGB1779[*u]);*r=(rtemp>0?(rtemp>255?255:(unsigned char)rtemp):0);*g=(rtemp>0?(gtemp>255?255:(unsigned char)gtemp):0);*b=(rtemp>0?(btemp>255?255:(unsigned char)btemp):0);b += 3;y ++;u ++;v ++;}if(u_buffer) free(u_buffer);if(v_buffer) free(v_buffer);return 0;}void InitLookupTable(){int i;for (i = 0; i < 256; i++) YUVRGB14075[i] = (float)1.4075 * (i-128);//采用查表法,-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++) YUVRGB1779[i] = (float)1.779* (i-128);}

3. yuv2rgb. h

#ifndef YUV2RGB_H_#define YUV2RGB_H_int YUV2RGB(int x_dim, int y_dim, void *y_in,void *u_in,void *v_in, void *rgb_out, int flip);void InitLookupTable();#endif





四.实验中遇到的错误



      由图中的红点可以看出,产生了数据泄露。由于unsignedchar的取值范围为0-255,所以小于0或者大于255的部分将不能保证其准确性,例如256会变为0。所以要对这部分数据进行二次赋值,若小于0则置为0,若大于255则置为255。程序如下:


 

五.实验结果

界面显示:用到了getchar();让程序运行结束后,停在了运行界面,按下键后才会返回编辑界面,否则运行界面一闪而过。


素材1:



素材2:



素材3:



素材4:



六.分析实验结果

    由宏块信息可以看出,转换前和转换后的YUV三个变量都有些许的不同。导致这种误差的可能因素有很多,比如转换公式的系数不够精准;将UV两个空间扩大的过程选择将同样的值赋给四个块的方法会出现误差;在RGB转YUV的过程中产生的误差。

七.结论

1.掌握了RGB与YUV相互间的转换方式以及YUV和RGB的存储格式。

2.复习了c语言文件,开buffer,以及指针的用法。

3.读懂RGB转YUV的程序,举一反三做出YUV转RGB的程序。

4.调试出问题的时候一定不要急躁,分析产生问题的原因加以修改。

 

 

0 0
原创粉丝点击