您的位置:首页 > 其它

数据压缩原理实验1_实验报告

2017-03-19 22:25 417 查看

数据压缩原理实验1_彩色空间转换实验(yuv转rgb)

一、实验原理

(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.140*V

G = Y - 0.394*U - 0.581*V

B = Y + 2.032*U

同样经过归一化处理,使其动态范围控制在-0.5—0.5之间,并让零电平对应码电平128,因此在代码中真正的RGB转换公式如下:

R=Y+1.1402*(V-128)

G=Y-0.34414*(U-128)-0.71414*(V-128)

B=Y+1.772*(U-128)

(3)YUV文件到RGB文件的转换

4:2:0格式是指色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半。

在文件存储中,RGB文件存储方式是每一个像素点都按照B、G、R的顺序进行存储,每一帧占用空间为width * height * 3个字节的空间。YUV文件存储方式为每一帧的YUV分开存储,先存Y,占用的空间为width * height,之后是U和V,所占用的空间分别为width * height /4。

要生成的RGB文件的取样格式为4:4:4,所以进行yuv到rgb的转换时,对给定的Y保持不变,对U和V进行上采样,使其可与RGB的存储空间相同。

二、实验流程分析

1.定义变量,设置参数,打开两个文件,申请缓冲区

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

3.将U、V块分别进行上采样,使其与Y块的大小相同

4.将4:4:4的Y、U、V通过转换公式计算得到R、G、B值并写出RGB文件

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

三、关键代码及分析

1、main2.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 RGB24 file
* required arg2 should be the output RAW YUV12 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* yuvFileName = NULL;
char* rgbFileName = NULL;
FILE* yuvFile = NULL;
FILE* rgbFile = NULL;
u_int8_t* yuvBuf = NULL;
u_int8_t* rgbBuf = NULL;
u_int32_t videoFramesWritten = 0;

/* begin process command line */
/* point to the specified file names */
yuvFileName = argv[1]; //rgb、yuv文件名(down.rgb down.yuv )和宽高(256、256)被赋值
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 the input buffer for a frame 申请内存空间 */
//因为色度格式是4:2:0,所以u、v都是每四个像素取一个,所以存储大小是一帧图像的1.5倍
yuvBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 1.5);

/* get an output buffer for a frame */
//每个像素有rgb三个分量,所以是3倍
rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);

if (rgbBuf == NULL || yuvBuf == NULL ) //判断buffer是否已满
{
printf("no enought memory\n");
exit(1);
}
//buffer:要读/写的数据块地址,size :要读/写的每个数据项的字节数 ,count :要读/写的数据项数量,fp :文件指针
//读入yuv文件,存在内存区,直到文件结束
while (fread(yuvBuf, 1, frameWidth * frameHeight * 1.5, yuvFile))
{
if(YUV2RGB(frameWidth, frameHeight, yuvBuf, rgbBuf, flip))  //判断函数是否运行成功
{
printf("error");
return 0;
}

fwrite(rgbBuf,1,frameWidth * frameHeight*3,rgbFile);

printf("\r...%d", ++videoFramesWritten); //  \r按回车换行
}

printf("\n%u %ux%u video frames written\n", videoFramesWritten, frameWidth, frameHeight); // \n自动换行
/* cleanup 关闭文件,释放缓存区*/
if (rgbFile != NULL)
fclose(rgbFile);

if (yuvFile != NULL)
fclose(yuvFile);

if (rgbBuf != NULL)
free(rgbBuf);

if (yuvBuf != NULL)
free(yuvBuf);

return(0);
}


2、
4000
yuv2rgb.cpp文件代码:

/**************************************************************************
*
*  rgb2yuv.c, 24-bit RGB bitmap to YUV converter
*
*  Copyright (C) 2001  Project Mayo
*
*  Adam Li
*
*  DivX Advance Research Center <darc@projectmayo.com>
*
**************************************************************************/

/* This file contains YUV to RGB transformation functions.                */
/* changetime 2017.3.18 */

#include "stdlib.h"
#include "yuv2rgb.h"

static float /*YUVRGB[256],*/ YUVRGB1402[256];
static float YUVRGB034414[256], YUVRGB071414[256];
static float  YUVRGB1772[256];

int YUV2RGB (int x_dim, int y_dim, void *yuv_in, void *rgb_out, int flip)//宽、高、yuvBuf、rgbBuf、 flip
{
static int init_done = 0;
long i, j, size;
unsigned char *r, *g, *b; //用于分别指向输出的rgb文件中的R、G、B三个部分
unsigned char *y, *u, *v; //用于分别指向读入的yuv文件中的Y、U、V三个部分
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;  //用于UV上采样的中间指针
/* unsigned char *y_buffer, *u_buffer, *v_buffer; */
unsigned char *rgb_buffer; //用于寄存输出rgb文件的缓冲区
unsigned char *sub_u_buf, *sub_v_buf; //用于寄存UV上采样后文件的缓冲区
float rk, gk, bk;  //用于判断数据是否溢出的中间变量

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
rgb_buffer = (unsigned char *)rgb_out;
//UV上采样后,变成444格式,大小和Y所占大小一样
sub_u_buf = (unsigned char *)malloc(size * sizeof(unsigned char));
sub_v_buf = (unsigned char *)malloc(size * sizeof(unsigned char));

//用yuv文件中y,u,v三个数据分别赋值。
y = (unsigned char *)yuv_in; //yuv文件中先存储y数据,然后是u,最后是v
u = y +size;
v = y +size +size/4;

// u,v上采样
for (i = 0; i < y_dim / 2; i++)
{
pu1 = sub_u_buf + 2 * i * x_dim;
pu2 = sub_u_buf + (2 * i + 1) * x_dim;
pv1 = sub_v_buf + 2 * i * x_dim;
pv2 = sub_v_buf + (2 * i + 1) * x_dim;
psu = u + i * x_dim / 2; //psu指向输入u文件中的每行第一个数据
psv = v + i * x_dim / 2; //psv指向输入v文件中的每行第一个数据
for (j = 0; j < x_dim / 2; j++)
{
*pu1 = *psu; *pu2 = *psu;
pu1++; pu2++;
*pu1 = *psu; *pu2 = *psu;
*pv1 = *psv; *pv2 = *psv;
pv1++; pv2++;
*pv1 = *psv; *pv2 = *psv;
pu1++; pu2++;
pv1++; pv2++;
psu++; psv++;
}
}
//yuv转rgb
b = rgb_buffer;  //rgb数据倒序穿插存储
g = b + 1;
r = b + 2;
for (i = 0; i < size; i++)
{

//bk,gk,rk 为中间变量,用于判断是否有数据溢出,将数据控制在0-255之间
bk = (*y + YUVRGB1772[*sub_u_buf ]);
gk = (*y - YUVRGB034414[*sub_u_buf ] - YUVRGB071414[*sub_v_buf ]);
rk = (*y + YUVRGB1402[*sub_v_buf ]);
if(bk>0 )
{
if(bk>255)
*b=255;
else
*b=(unsigned char)bk;
}
else
*b=0;
/*   *b = (bk>0 ? (bk>255 ? 255 : (unsigned char)bk) : 0);  */
*g = (gk>0 ? (gk>255 ? 255 : (unsigned char)gk) : 0);  //条件运算符
*r = (rk>0 ? (rk>255 ? 255 : (unsigned char)rk) : 0);
b += 3;
g += 3;
r += 3;
y++;
sub_u_buf++;
sub_v_buf++;
}
//啊啊啊啊,要释放sub_u_buf,sub_v_buf指向的两个缓存空间,必须要把指针指到空间的开头,之前的循环,指针指在了缓存空间最后的数的下一个!!!
sub_u_buf -= size;
sub_v_buf -= size;
if (sub_u_buf)
free(sub_u_buf);
if (sub_v_buf)
free(sub_v_buf);
/* if (rgb_buffer)
free(rgb_buffer);  */

return 0;
}

void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) YUVRGB1402[i] = (float)1.402 * (i-128);
for (i = 0; i < 256; i++) YUVRGB034414[i] = (float)0.34414 * (i-128);
for (i = 0; i < 256; i++) YUVRGB071414[i] = (float)0.71414 * (i-128);
for (i = 0; i < 256; i++) YUVRGB1772[i] = (float)1.772 * (i-128);
}


3、yuv2rgb.h文件代码:

#ifndef YUV2RGB_H_
#define YUV2RGB_H_
int YUV2RGB (int x_dim, int y_dim, void *yuv_in, void *rgb_out, int flip);

void InitLookupTable();

#endif


四、实验结果及分析

1、程序运行成功后,会跳出如下对话框



用yuv播放器打开原始yuv文件(左)和转换后的yuv文件(右)

(1)



(2)



(3)



(4)



2、错误及分析

(1)yuv文件中先存储完y的所有数据,然后是u,最后是v。

rgb文件中是按照b g r 的顺序穿插存储。

(2)要有bk,gk,rk中间变量,以将转换后的rgb数据控制在[0~255]范围内。(小于0则等于0,大于255则等于255)

(3)释放缓存空间要把指向空间的指针指在空间的第一个数据。

(4)yuv2rgb.cpp文件中的指针*rgb_buffer是指向main2.cpp文件中的空间 rgbBuf ,所以释放空间的操作应在main2.cpp文件中进行。

五、结论

本次实验主要是进行了RGB与YUV文件之间的转换,帮助理解了彩色空间的概念并掌握了不同彩色空间转换的基本方程,加深了对数据类型转换、指针和缓冲区联系的认识。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: