一.基本原理
(1)RGB转YUV的公式
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之间,需要进行归一化,对色差信号引入压缩系数:
U=-0.1684R -0.3316G+0.5B
V=0.5R -0.4187G -0.0813B
(2)YUV转RGB的公式
R=Y+1.4075(V-128)
G=Y-0.3455(U-128)-0.7169(V-128)
B=Y+1.779(U-128)
(3)分析
RGB文件中的数据存储方式:B1G1R1B2G2R2B3G3R3…
4:2:0YUV文件中数据的存储方式:Y1Y2…Y(256x256)U1U2…U(256x256x0.25)V1V2…V(256x256x0.25)
RGB转YUV格式时需要下采样,每2x2个像素单元公用一个U一个V;而YUV转RGB格式时需要上采样,每2x2个像素单元中,需要将共同的U和V分别复制给另外三个像素。
二.实验流程
(1)读入待转换的文件名,文件的宽高,设置输出的文件名
初始化main函数参数:项目名右键->属性->配置属性->调试->设置命令参数和工作目录
(2)初始化参数
FILE *rgbFile
FILE *yuvFile
int width,height
unsigned char *rgbBuffer
unsigned char *yBuffer,*uBuffer,*vBuffer
(3)打开两个文件(fopen),变量赋值,开buffer(malloc())
(4)将YUV文件的数据读入yBuffer,uBuffer,vBuffer,写rgbBuffer
(5)输出rgb文件
三.关键代码及分析
yuv2rgb.h
#ifndef YUV2RGB_H_
#define YUV2RGB_H_
int YUV2RGB (int x_dim, int y_dim, void *rgb_out, void *y_in, void *u_in, void *v_in, int flip);
void InitLookupTable();
#endif
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
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 */
/* internal variables */
char* yuvFileName = NULL;//初始化参数
char* rgbFileName = NULL;
FILE* yuvFile = NULL;
FILE* rgbFile = 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);//4:2:0取样,UV分量都只有Y分量的1/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))
{
if(YUV2RGB(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip))
{
printf("error");
return 0;
}
fwrite(rgbBuf, 1, frameWidth * frameHeight*3, rgbFile);
printf("\r...%d", ++videoFramesWritten);
}
printf("\n%u %ux%u video frames written\n",
videoFramesWritten, frameWidth, frameHeight);
/* cleanup */
if(rgbFile) fclose(rgbFile);//判断后再关闭文件或释放空间
if(yuvFile) fclose(yuvFile);
if (yBuf) free(yBuf);
if (uBuf) free(uBuf);
if (vBuf) free(vBuf);
if (rgbBuf) free(rgbBuf);
getchar();//让界面卡住,显示在那里,等待输入任意字符后,程序才会结束退出,方便查看结果。
return(0);
}
yuv2rgb.cpp
#include "stdlib.h"
#include "yuv2rgb.h"
static float YUVRGB14075[256],YUVRGB03455[256];
static float YUVRGB07169[256],YUVRGB17790[256];
int YUV2RGB (int x_dim, int y_dim, void *rgb_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;
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
unsigned char *y_buffer, *u_buffer, *v_buffer;
unsigned char *sub_u_buf, *sub_v_buf;
short tr,tg,tb;//为防止数据溢出定义中间变量(中间变量存储的数值范围要大于256)
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
b= (unsigned char *)rgb_out;
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));
y= y_buffer;
u= u_buffer;
v= v_buffer;
if (!(u_buffer&&v_buffer))
{
if (u_buffer) free(u_buffer);
if (v_buffer) free(v_buffer);
return 2;
}
//上采样
for (j = 0; j < y_dim/2; j ++)
{
psu = sub_u_buf + j * x_dim / 2;//psu、psv指向已经下采样的YUV文件中的U、V首地址
psv = sub_v_buf + j * x_dim / 2;
pu1 = u_buffer + 2 * j * x_dim;//在上采样中,pu1、pv1指向偶数行第一个元素,pu2、pv2指向奇数行第一个元素
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;//将psu复制给2x2的像素单元
*(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)
{
for (i = 0; i < size; i++)
{
g = b + 1;
r = b + 2;
tr=*y + YUVRGB14075[*v];
tg=*y - YUVRGB03455[*u] - YUVRGB07169[*v];
tb=*y + YUVRGB17790[*u];//先将计算出的RGB存入中间变量
if(tr<0) *r=0;
else if(tr>255) *r=255;
else *r = (unsigned char)(tr);
if(tg<0) *g=0;
else if(tg>255) *g=255;
else *g = (unsigned char)(tg);
if(tb<0) *b=0;
else if(tb>255) *b=255;
else *b = (unsigned char)(tb);//加判断防止数据溢出
b += 3;
y ++;
u ++;
v ++;
}
}
free(u_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++) YUVRGB17790[i] = (float)1.779 * (i-128);//提前减去128保证UV的在-128~127之间
}
四.实验结果及分析
(1)未添加中间变量防止数据溢出时的结果
溢出的地方会变成红色
(2)加了中间变量后
误差部分的得到了修正
(3)处理前的YUV文件与处理后的YUV文件对比
因为程序中用到了一些数据类型转换并且做了防止溢出的处理,所以处理前后的文件在具体数值上会有少许差异,但是这种差别肉眼是无法分辨的。