您的位置:首页 > 其它

数据压缩 彩色空间转换

2017-03-07 19:10 141 查看

彩色空间转换

实验原理

YUV到RGB空间的转换公式

由电视原理知识可知,数字视频系统中对色差信号的压缩公式为:

Cr=0.713(R−Y)

Cb=0.564(B−Y)

此时色差信号经过归一化处理,动态范围为-0.5-0.5,让色差零电平对应码电平128(8比特量化时),则有:

V=0.713(R−Y)+128

U=0.564(B−Y)+128

易推得YUV到RGB空间的转换公式为:

R=Y+1.4075(V−128)

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

B=Y+1.779(U−128)

取样格式

YUV空间的取样格式为4:2:0,U、V两个色差信号的亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半。而RGB空间的取样格式为4:4:4,则需要对U、V分量做上采样,让水平方向和垂直方向上取样点数与Y一致。

存储方式与占用空间

YUV格式的视频将整帧图像的Y打包存储,依次再存整帧的U、V,然后再是下一帧的数据;YUV格式按4:2:0取样的视频所占空间大小为Width*Height*Frame*1.5(Width –宽、Height–高、Frame–帧数)。RGB格式的视频按每个像素B、G、R的顺序储存数据;所占空间大小为Width*Height*Frame*3。

YUV转换为RGB后颜色溢出的处理

经过上式计算,原来为整数的Y、U、V转换为浮点数R、G、B,由于RGB文件的颜色是8bit量化,则数值不在0-255范围内的颜色会出现溢出,显示错误。本实验采用了定义float型的中间变量存放计算RGB的直接结果,而后通过简单的判断防止颜色溢出。

带参数的主函数的参数输入

打开项目属性窗口,通过设置工作目录、命令参数完成主函数的参数输入。



YUV2RGB文件转换流程分析

程序初始化(打开两个文件、定义变量和缓冲区 等)

读取YUV文件,抽取YUV数据写入缓冲区

调用YUV2RGB的函数实现YUV到RGB数据的转换

写RGB文件

程序收尾工作(关闭文件,释放缓冲区)

关键代码及其分析

分析部分写在代码注释中


YUVtoRGB.cpp

#include "stdlib.h"
#include "YUVtoRGB.h"
static float YUVRGB14075[256];
static float YUVRGB03455[256];
static float YUVRGB07169[256];
static float YUVRGB1779[256];
int YUV2RGB (int Width, int Height, void *rgb_out, void *y_in, void *u_in, void *v_in)
{
static int init_done = 0;
long i, j, size;
unsigned char *r, *g, *b;
float rf, gf, bf;//中间变量,用来防止色彩溢出
unsigned char *y, *u, *v;
unsigned char *y_buffer, *u_buffer, *v_buffer,*rgb_buffer;
unsigned char *sub_u_buf, *sub_v_buf;

if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}

// check to s
d2d0
ee if width and height are divisible by 2
if ((Width % 2) || (Height % 2)) return 1;
size = Width * Height;
y_buffer = (unsigned char *)y_in;
u_buffer = (unsigned char *)u_in;
v_buffer = (unsigned char *)v_in;
rgb_buffer = (unsigned char *)rgb_out;
sub_u_buf = (unsigned char *)malloc(Width*Height);
sub_v_buf = (unsigned char *)malloc(Width*Height);
//sub_u_buf、sub_v_buf用于存放宽高都上采样以后的U/V,便于计算RGB

/************************上采样***********************/
for (i = 0; i < Height; i++)
{
for (j = 0; j < Width; j++)
{
*(sub_u_buf + i*Width + j) = *(u_buffer + (i / 2)*Width/2 + j / 2);
*(sub_v_buf + i*Width + j) = *(v_buffer + (i / 2)*Width /2+ j / 2);
}
}
/****************************************************/

b = rgb_buffer;//通过r/g/b三个指针改变rgb_buffer的内容值
y = y_buffer;
u = sub_u_buf;
v = sub_v_buf;
for (i = 0; i < Height; i++)
{
for (j = 0; j < Width; j++)
{
g = b + 1;//RGB格式文件储存按照BGR的顺序
r = b + 2;
rf= (*y + YUVRGB14075[*v]);
gf = (*y - YUVRGB03455[*u] - YUVRGB07169[*v]);
bf = (*y + YUVRGB1779[*u]);
*r =(rf>0?(rf>255? 255:(unsigned char)rf):0);
*g = (gf>0 ? (gf>255 ? 255 : (unsigned char)gf) : 0);
*b = (bf>0 ? (bf>255 ? 255 : (unsigned char)bf) : 0);
/*由YUV到RGB的转换公式可知,经过计算得到的RGB值都不是整数,由于计算
系数的精度,RGB值有可能出现超出0-255的范围。由于unsigned char型
数据占1字节,超出0-255的数值会溢出,发生彩色显示错误。例如256会变为
0。
可以采用一个float型中间变量(rf/bf/gf)来暂存由彩色转换公式计算得
来的RGB值,经过判断以后,再给unsigned char型的r/g/b进行赋值,大
于255则赋值为255,小于0则赋值为0,在0-255范围内的数值则进行强制类
型转换。*/
b += 3;
y++;
u++;
v++;
}
}

if (sub_u_buf != NULL)  free(sub_u_buf);
if (sub_v_buf != NULL)  free(sub_v_buf);
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.779 * (i-128);
}
/*提前计算彩色转换公式中的分量值。需要特别注意的是UV值需要提前
减128,也就是说,计算分量值时保证UV的范围在-128~127。*/


main.cpp

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include"YUVtoRGB.h"
#define u_int8_t    unsigned __int8
#define u_int       unsigned __int32
#define u_int32_t   unsigned __int32
//为了防止不同编程软件数据类型int的字节数不一致,利用define进行规定
int main(int argc,char ** argv)
{
u_int frameWidth = 352;
u_int frameHeight = 240;
int i;

char *rgbFileName= NULL;
char *yuvFileName= NULL;
FILE *rgbFile=NULL;
FILE *yuvFile=NULL;
u_int8_t* rgbBUFFER = NULL;
u_int8_t* yBUFFER = NULL;
u_int8_t* uBUFFER= NULL;
u_int8_t* vBUFFER = NULL;
u_int32_t videoFramesWritten = 0;
yuvFileName = argv[1];
rgbFileName = argv[2];
frameWidth = atoi(argv[3]);
frameHeight = atoi(argv[4]);
//argv[0]是缺省的,软件自动会装填Project当中的exe文件名
yuvFile = fopen(yuvFileName, "rb");
if (yuvFile == NULL)
{
printf("cannot find yuv file\n");
exit(1);
}
else
{
printf("The output yuv file is %s\n", yuvFileName);
}

rgbFile = fopen(rgbFileName, "wb");
if (rgbFile == NULL)
{
printf("cannot find rgb file\n");
exit(1);
}
else
{
printf("The input rgb file is %s\n", rgbFileName);
}

/* get an input buffer for a frame */
rgbBUFFER= (u_int8_t *)malloc(frameWidth * frameHeight * 3);
/* get the output buffers for a frame */
yBUFFER= (u_int8_t *)malloc(frameWidth * frameHeight);
uBUFFER= (u_int8_t *)malloc(frameWidth * frameHeight/4);
vBUFFER= (u_int8_t *)malloc(frameWidth * frameHeight/4);

if (rgbBUFFER == NULL || yBUFFER == NULL || uBUFFER == NULL || vBUFFER == NULL)
{
printf("no enought memory\n");
exit(1);
}
while(fread(yBUFFER,1,frameWidth*frameHeight,yuvFile)
&&fread(uBUFFER,1,frameWidth*frameHeight/4,yuvFile)
&&fread(vBUFFER,1,frameWidth*frameHeight/4,yuvFile))
/*当读入成功时fread函数返回实际读取的数据项个数,当整帧的YUV都能读
入时,进入while循环。由于文件指针自动向后移动到下一个未读字节,故该
程序也适用于视频文件,整段视频被读完fread函数返回0,则结束循环。*/
{
if(YUV2RGB(frameWidth, frameHeight, rgbBUFFER, yBUFFER, uBUFFER, vBUFFER))
{
printf("error");
return 0;
}
fwrite(rgbBUFFER, 1, frameWidth * frameHeight*3, rgbFile);
printf("\r...%d", ++videoFramesWritten);
}

printf("\n%u %ux%u video frames written\n",
videoFramesWritten, frameWidth, frameHeight);
if(rgbFile!=NULL)   fclose(rgbFile);
if(yuvFile!=NULL)   fclose(yuvFile);
if(rgbBUFFER!=NULL) free(rgbBUFFER);
if(yBUFFER!=NULL)   free(yBUFFER);
if(uBUFFER!=NULL)   free(uBUFFER);
if(vBUFFER!=NULL)   free(vBUFFER);
return(0);
}


实验结果及其分析

将转换得的RGB文件输入已有的RGB2YUV程序,得到新的YUV文件,用YUVplayer打开新的YUV文件查看结果。

down.yuv






由于计算系数精度、强制类型转换等带来的误差,经过彩色空间转换的YUV值会存在些许差异,但从画面、数据上看,这个差异都不算太明显。

没有采用中间变量,彩色溢出的情况




下面给出另外三个YUV视频文件进行彩色空间转换的结果,佐证该程序是正确的。

akiyo.yuv






src01.yuv






src04.yuv




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  RGB yuv 数据压缩