YUV2RGB实验报告

    xiaoxiao2021-03-25  103

    1.实验基本原理

    RGB2YUV转换公式:

    Y=0.2990*R+0.5870*G+0.1140*B

    U=-0.1684*R-0.3316*G+0.5*B+128

    V=0.5*R-0.4187*G-0.813*B+128

    YUV2RGB转换公式:

    B=Y+1.779(U-128)

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

    R=Y+1.14075(V-128)

    YUV文件格式:

    YUV按照4:2:0的方式取样,即色度信号在水平方向和垂直方向的取样频率是亮度信号的1/4。

    先存储一帧中的所有Y数据,接着存储一帧中所有的U数据,再存储所有的V数据。

    所占空间大小:weight*height*1(Y)+weight*height/4(U)+weight*height/4(V)=weight*height*3/2(byte)

    RGB文件格式:

    RGB按照4:4:4的方式取样。

    顺序存储每一像素点的B,G,R数据。

    所占空间大小:weight*height*1(B)+weight*height*1(G)+weight*height*1(R)=weight*height*3(byte)

    因为两种文件格式大小不一样,所以在RGB转YUV时色差信号需要下采样,即经公式转换后,每相邻四个U值取平均作为最终U值进行存储。V同理。反之,在YUV转RGB时,每个色差信号值需作为相邻4个像素的U,V值,即上采样。

    关于数据溢出:

    由于RGB文件是8bit量化,而YUV文件经公式转化后数值有可能超出0~255范围,数据溢出(如256会变为0)会造成图像部分地方颜色显示错误。可以通过设置double型(字节数比unsigned char型多)中间变量存放转换结果,再通过if语句判断是否超出范围来确定R,G,B的最终值,防止数据溢出。

    关于main函数:

    带参数的main函数书写:int main(int argc,char* argv[ ])

    其中argc为送入参数的个数,本实验中为5个;

    argv[0]-->yuv2rgb.exe(缺省)

    argv[1]-->up.yuv(输入文件)

    argv[2]-->up.rgb(生成文件)

    argv[3]-->256(输入文件宽)

    argv[4]-->256(输入文件高)

    具体操作:

    项目属性-->配置属性-->调试-->更改参数命令和工作目录(文件打开路径)

    关于输出:

    编译通过后输出屏无显示,在return(0)前添加一行getchar( )

    当程序执行到getchar()或者getch()时会停下来等待输入,这样便于在以后直接运行可执行程序时可以看到结果,如果不用的话可能程序执行会一闪而过,导致观察不到。

    关于vs2013环境:

    运行程序时,会出现如下错误:

    这是由于vs2013编译环境造成的,这时需要如下操作:工程属性-->配置属性-->c/c++-->预处理器定义栏内编辑,在下方增添一条

    _CRT_SECURE_NO_WARNINGS指令。保存再次编译会显示通过。

    2.实验流程分析

    a.读入转换文件的文件名:宽、高、输入及输出文件名

    b.初始化参数:FILE*rgbFile;

                             FILE* yuvFile;

                             int weight,height;

                             unsigned char* rgbBuffer malloc();

                             unsigned char* Y-Buffer ,*U-Buffer,*V-Buffer;

    c.打开两个文件,赋值变量,开buffer(RGB文件1个,YUV文件3个)

    d.读YUV文件,调用函数进行格式转换,写RGB文件

    e.关闭文件,释放缓冲区

    3.关键代码及分析

    main.cpp

    #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include "yuv2rgb.h" #define u_int8_t unsigned __int8 //定义数据类型,防止不同编译软件数据类型字节数的不一致 #define u_int unsigned __int32 #define u_int32_t unsigned __int32 #define FALSE false #define TRUE true /* * yuv2rgb * required arg1 should be the input RAW YUV12 file * required arg2 should be the output RAW RGB24 file */ int main(int argc, char** argv) { /* variables controlable from command line */ u_int frameWidth = 352; /* --width=<uint> */ u_int frameHeight = 240; /* --height=<uint> */ bool flip = TRUE; /* --flip */ unsigned int i; /* internal variables */ 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; /* begin process command line */ /* point to the specified file names */ yuvFileName = argv[1]; rgbFileName = argv[2]; frameWidth = atoi(argv[3]); //字符串强制转整型 frameHeight = atoi(argv[4]); /* 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); } /* get an input buffer for a frame */ rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3); //开辟缓冲区 /* get the output buffers 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); 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)) //读入一帧的YUV,指针自动移至下一个未读字节处,即下一帧的开始,故可实现视频读入 { if (YUV2RGB(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip)) { printf("error"); return 0; } printf("\r...%d", ++videoFramesWritten); fwrite(rgbBuf, 1, frameWidth * frameHeight * 3, rgbFile); } printf("\n%u %ux%u video frames written\n", videoFramesWritten, frameWidth, frameHeight); /* clean up */ if (yuvFile!=NULL) fclose(yuvFile); if (rgbFile != NULL) fclose(rgbFile); if (yBuf != NULL) free(yBuf); if (uBuf != NULL) free(uBuf); if (vBuf != NULL) free(vBuf); //关闭文件,释放缓冲区,防止内存泄漏 getchar(); return(0); }

    yuv2rgb.cpp

    /* This file contains YUV to RGB transformation functions. */ #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 *bmp_out, void *y_in, void *u_in, void *v_in, int flip) { static int init_done = 0; long i, j, size; unsigned char *r, *g, *b; unsigned char *y, *u, *v; double r1, g1, b1; //设置中间变量,防止数据溢出 unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv; unsigned char *y_buffer, *u_buffer, *v_buffer; unsigned char *sub_u_buf, *sub_v_buf; if (init_done == 0) { InitLookupTable(); init_done = 1; } // check to see if x_dim and y_dim are divisible by 2 if ((x_dim % 2) || (y_dim % 2)) return 1; size = x_dim * y_dim; // allocate memory 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 *)bmp_out; y = y_buffer; u = u_buffer; v = v_buffer; // subsample 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) = *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; } } // convert YUV to RGB if (!flip) //倒序存储的YUV转RGB { } else{ for (i = 0; i < size; i++) { g = b + 1; r = b + 2; b1 = (*y) + YUVRGB1779[*u]; g1 = (*y) - YUVRGB03455[*u] - YUVRGB07169[*v]; r1 = (*y) + YUVRGB14075[*v]; if(b1>0&&b1<255) //判断数据是否溢出 *b = (unsigned char)b1; else if (b1>255) *b = 255; else *b = 0; if (g1>0 && g1<255) *g = (unsigned char)g1; else if (g1>255) *g = 255; else *g = 0; if (r1>0 && r1<255) *r = (unsigned char)r1; else if (r1>255) *r = 255; else *r = 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); 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.799 * (i - 128); }

    4.实验结果及分析

    将程序生成的RGB文件通过已有程序进行RGB2YUV转换,打开YUV播放器查看结果并与原图像作对比。

    有数据溢出时的情况:

    可以看出UV第4列的值与原文件相差较大,即出现数据溢出,图像部分区域有红点出现。

    在经过溢出判断处理后,红点得到消除,结果如下:

    两幅图像数据差异很小,人眼无法分辨出图像区别。

    将另外3个yuv视频文件对该实验进行验证,结果如下:

    视频1:

    视频2:

    视频3:

    

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

    最新回复(0)