实验一 |彩色空间rgb和yuv的相互转换

彩色空间转换

一、实验目的

1.基本要求:编写RGB转化为YUV程序,重点掌握函数定义,部分查找表的初
始化和调用,缓冲区分配。将得到的RGB文件转换为YUV文件,用YUV Viewer播放器观
看,验证是否正确。
2.提高要求:编写将YUV转换为RGB的程序。将给定的实验数据用该程序转换
为RGB文件。并与原RGB文件进行比较,如果有误差,分析误差来自何处。

二、转换公式

1、RGB to 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;

再给UV分量各+128去掉负值,使变化范围都在[0,255]之间,最终得出公式:
Y=0.2990R+0.5870G+0.1140B;
U=-0.1684R-0.3316G+0.5B+128;
V=0.5R-0.4187G-0.0813B+128;

2、YUV to RGB
将前面的式子反解出rgb即可,且再给UV分量各-128代入计算才能得到正确范围的值:
R=Y+1.4075(V-128);
G=Y-0.3455(U-128)-0.7169(V-128);
B=Y+1.779(U-128);

注意:rgb的图像是倒像的,转换成yuv所用的数据也是反着排,所以需要反转一下得到正着的图像。

三、主要思路

首先明确程序分为两个主要部分:

  • 在main文件里实现文件的读写和函数的调用;
  • 在rgb2yuv函数实现空间转换的算法。

1、RGB to YUV
读入的文件,输出的文件和宽、高都以参数形式设置,可以直接fread已有rgb文件,我们给原rgb文件整体开辟一个存储空间rgbBuf,并读取其数据;yuv分别开辟三个空间,以便指针的设置。
通过计算将每个像素的rgb数据转化为yuv后,此时的yuv各为256256的大小,而真正的uv分别为128128,是计算所得的1/4,因此需要进行下采样,每四个U/V形成一个新的U/V,最后输出数据存入down256x256_converted.yuv中。
根据老师给的标准程序,形成一个思路梗概:
在这里插入图片描述

2、RGB to YUV
我们给原yuv文件整体开辟一个存储空间yuvBuf,再4:2:2的格式下,y为256256,uv分别为128128,因此需要进行上采样,每一个U/V的值要赋给其周围的三个点,最终也形成256*256的数据空间。此时可以利用公式对其进行rgb转化,最后输出数据存入down256x256_converted.rgb中。
在这里插入图片描述

四、查找表的使用

所有需要计算的数r、g、b、y、u、v都在[0,255]取值,因此可以利用查找表将所有可能的计算结果全部算出来,并声明,然后直接使用:
1、RGB to YUV

static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];

算出所有可能值并作为查找表:

void InitLookupTable()
{
	int i;

	for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;
	for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;
	for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;
	for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;
	for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;
	for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;
	for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i;
}

这使得程序效率大大提高:

*y = (unsigned char)(  RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2          + 128);
*v = (unsigned char)(  (*r)/2          - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);

2、YUV to RGB

static float YUVRGB14075[256];
static float YUVRGB03455[256], YUVRGB07169[256];
static float YUVRGB17790[256];

查找表:

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.7790 * (i-128);
}


五、程序实现

1、RGB to YUV
老师已经给出完整程序,此处给出我认为关键的地方。
转换函数:

// convert RGB to YUV
	if (!flip) {
		for (j = 0; j < y_dim; j ++)
		{
			y = y_buffer + (y_dim - j - 1) * x_dim;
			u = u_buffer + (y_dim - j - 1) * x_dim;
			v = v_buffer + (y_dim - j - 1) * x_dim;

			for (i = 0; i < x_dim; i ++) {
				g = b + 1;
				r = b + 2;
				*y = (unsigned char)(  RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
				*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2          + 128);
				*v = (unsigned char)(  (*r)/2          - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
				b += 3;
				y ++;
				u ++;
				v ++;
			}
		}
	} else {
		for (i = 0; i < size; i++)
		{
			g = b + 1;
			r = b + 2;
			*y = (unsigned char)(  RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
			*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2          + 128);
			*v = (unsigned char)(  (*r)/2          - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
			b += 3;
			y ++;
			u ++;
			v ++;
		}
	}

下采样:

// 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 ++)
		{
			*psu = (*pu1 + *(pu1+1) + *pu2 + *(pu2+1)) / 4;
			*psv = (*pv1 + *(pv1+1) + *pv2 + *(pv2+1)) / 4;
			psu ++;
			psv ++;
			pu1 += 2;
			pu2 += 2;
			pv1 += 2;
			pv2 += 2;
		}
	}

2、YUV to RGB
main函数
分别为准备生成的rgb文件和需要处理的yuv文件分配buffer——rgbBuf、yuvBuf。yBuf用于代表y的数据部分,这256x256个数据不需要后续处理可以直接用。

// internal variables
	char* rgbFileName = NULL;
	char* yuvFileName = NULL;
	FILE* rgbFile = NULL;
	FILE* yuvFile = NULL;
	unsigned char* rgbBuf = NULL;
	unsigned char* yuvBuf = NULL;
	unsigned char* yBuf = NULL;
	u_int32_t videoFramesWritten = 0;
yuvBuf = (unsigned char*)malloc(frameWidth * frameHeight * 3/2);
rgbBuf = (unsigned char*)malloc(frameWidth * frameHeight * 3);
yBuf = (unsigned char*)malloc(frameWidth * frameHeight);

根据示例,打开文件时进行一些基于程序严谨性的周全的限制:

// 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);
	}
	

考虑buffer为空的情况:

   if (yuvBuf == NULL || rgbBuf == NULL || yBuf == NULL)
	{
		printf("no enought memory\n");
		exit(1);
	}
	YUV2RGB(frameWidth, frameHeight, yBuf,rgbBuf);
	if (rgbBuf==NULL)
	{
		printf("wrong change\n");
	}
	

读写关文件:


	fread(yuvBuf, sizeof(unsigned char), frameWidth * frameHeight * 3/2, yuvFile);
	
	fwrite(rgbBuf, sizeof(unsigned char), frameWidth * frameHeight * 3, rgbFile);

    fclose(rgbFile);
	fclose(yuvFile);

头文件yuv2rgb.h处的声明

int YUV2RGB (int x_dim, int y_dim, unsigned char *ymp,unsigned char *rgb_out);

void InitLookupTable();

上采样:
原理如图:
在这里插入图片描述

设置psu psv指向yuvBuf的128*128的UV两个数据区间的首数据地址,pu1、pu2和pv1、pv2分别指向为上采样建立的数据空间up_u_buf和up_v_buf的首数据地址。
yuvBuf在函数部分表示为ymp指针指向的256x256x2/3的区域;rgbBuf在函数部分表示为rgb_out指针指向的256x256x3的区域。

long i, j, size;//size为x_dim * y_dim;
unsigned char *y, *u, *v;
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
unsigned char *up_u_buf, *up_v_buf;
unsigned char *rgb_buffer;
int r, g, b;
up_u_buf = (unsigned char *)malloc(size * sizeof(unsigned char));
up_v_buf = (unsigned char *)malloc(size * sizeof(unsigned char));
y = (unsigned char *)ymp;
u = ymp+size;
v = ymp+size+size/4;
rgb_buffer=rgb_out;

下面进行上采样:

// upsample UV
	 for(i = 0; i < y_dim/2; j ++)
	 {
		 psu = u + i * x_dim / 2;//psu psv分别指向原u v
         psv = v + i * x_dim / 2;
         pu1 = up_u_buf + 2 * i *x_dim;//pu1 pu2 /pv1 pv2分别指向u/v的奇数 偶数行
         pu2 = up_u_buf + (2 * i + 1) * x_dim;
         pv1 = up_v_buf + 2 * i *x_dim;
         pv2 = up_v_buf + (2 * i + 1) * x_dim;
		 for (j = 0; j < x_dim/2; j ++)
		 {
			 *pu1=*psu;//赋值给周围4宫格
			 *(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;//指向下一个4宫格
		 }
	 }
	                 

转换函数

	// convert YUV to RGB
	for (j = 0; j < y_dim; j ++)
	{
		for (i = 0; i < x_dim; i ++)
		{
			r =  *y + YUVRGB14075[*up_v_buf];
			g =  *y - YUVRGB03455[*up_u_buf]- YUVRGB07169[*up_v_buf];
			b =  *y + YUVRGB17790[*up_u_buf];
			//防溢出
			if(r>255) r=255;  if(r<0) r=0;
            if(g>255) g=255;  if(g<0) g=0;
            if(b>255) b=255;  if(b<0) b=0;
			*rgb_buffer =  unsigned char(b);
			*(rgb_buffer + 1) = unsigned char(g);
			*(rgb_buffer + 2) = unsigned char(r);
			y ++;//转到下一个yuv数据组
            up_u_buf ++;
            up_v_buf ++;
            rgb_buffer +=3;//后移bgr共三位
		}
	}

六、实验结果

原rgb:
在这里插入图片描述

转换为yuv:
在这里插入图片描述

原yuv:
在这里插入图片描述

转换为rgb:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值