实验一:彩色空间转换(YUV2RGB)

实验一:彩色空间转换

#ifndef/#define/#endif 使用详解:http://blog.csdn.net/abc5382334/article/details/18052757

Debug与Release版本的区别详解:http://blog.csdn.net/ithzhang/article/details/7575483#comments

VS中Release和Debug模式的区别:http://blog.csdn.net/eric491179912/article/details/6154375

YUV格式分析:http://www.cnblogs.com/armlinux/archive/2012/02/15/2396763.html

一、实验基本原理

1.    彩色空间转换的基本思想及转换公式

(1)   YUVRGB空间的相互转换

RGB格式转YUV格式:

由电视原理知识,

Y = 0.2990 R + 0.5870 G + 0.1140 B

R-Y=0.7010 R - 0.5870 G - 0.1140 B

B-Y=-0.2990 R-0.5870 G + 0.8860 B

为了使色差信号的动态范围控制在-0.50.5之间,需要进行归一化,对色差信号引入压缩系数,归一化后的色差信号为:

U = - 0.1684 R - 0.3316 G + 0.5 B

V = 0.5 R - 0.4187 G - 0.0813 B

(2)   码电平分配及数字表达式

亮电平信号:在对分量信号进行8比特均匀量化时,共分为256个等间隔的量化级。为了防止信号变动造成过载,在256级上端留20级,下端留16级作为信号超越动态范围的保护带。

色差信号:量化后码电平分配色差信号经过归一化处理后,动态范围为-0.50.5,让色差零电平对应码电平128,色差信号总共占225个量化级。在256级上端留15级,下端留16级作为信号超越动态范围的保护带。

由此可知,YUVRGB公式为:

                          R = Y + 1.4075 *V-128

G = Y 0.3455 *U128) – 0.7169 *V128

B = Y + 1.779 *U128

(3)   色度格式

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

所以:

RGB转换YUV格式时,根据公式计算得到一帧YUV数据后,要对色差信号UV进行下采样。

YUV转换RGB格式时,在利用公式进行计算之前首先要对色差信号UV进行上采样。(下面是两张丑丑的图示~)




 

二、实验流程

1.    建立工程,C相关知识回顾(养成良好代码书写习惯)

1)     程序文件一般分为三部分:

(1)头文件:包括与结构(类)的声明和使用这些结构(类)的函数的原型,不要将函数的定义或变量的声明放在头文件中,#ifndef/#define/#endif 防止头文件被多次引用,详解见文章头部链接。

(2)源代码文件:包含与结构(类)有关的函数的代码,即函数的定义

(3)源代码文件(main.c/cpp):包含调用这些函数的代码

2)     学会使用带参主函数定义以及命令参数、可执行文件工作路径的设置

VS2013步骤为:项目项目属性配置属性调试命令参数(编辑)/工作目录(编辑:绝对路径/浏览:找到具体文件夹目录)如下图:


      

        关于参数设置过程中DebugRelease版本的区别详解链接见文章头部链接。


2.    调试RGB转换YUV程序

理解课上所给例程,尤其对色差信号进行4:2:0下采样部分,妙用指针可以很容易实现,在课程作业编写YUV转换RGB程序中对UV数据进行上采样也是借鉴了例程的指针用法。

3.    编写YUV转换RGB程序,具体见下一部分。

三、关键代码

#include "stdlib.h"
#include "yuv2rgb.h"
/*YUV->RGB 转换公式
R = Y + 1.4075 *(V-128)
G = Y – 0.3455 *(U –128) – 0.7169 *(V –128)
B = Y + 1.779 *(U – 128)
*/
static float YUV2RGB14075[256];
static float YUV2RGB03455[256], YUV2RGB07169[256];
static float YUV2RGB1779[256];

/************************************************************************
*
*  int YUV2RGB (int x_dim, int y_dim, void *ychunk, void *uchunk, void *vchunk, void *rgb_out, int flip)
*
*	Purpose :	It takes a  YUV (4:2:0) format and convert it into
*				a 24-bit RGB bitmap.
*
*  Input :		x_dim	        the x dimension of the bitmap
*				y_dim	        the y dimension of the bitmap*				
*				yuv chunk		pointer to the YUV structure
				rgb_out		    pointer to the buffer of the bitmap
*
*  Output :	0		OK
*			1		wrong dimension
*			2		memory allocation error
*
*	Side Effect :
*				None
*
*	Date :		03/11/2017
*
************************************************************************/

int YUV2RGB(int x_dim, int y_dim, void *ychunk, void *uchunk, void *vchunk, void *rgb_out, 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 *addu_buffer, *addv_buffer;
	unsigned char *addu_buf, *addv_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	
	addu_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
	addv_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
	b = (unsigned char *)rgb_out;
	y = (unsigned char *)ychunk;	
	u = (unsigned char*)uchunk;
	v = (unsigned char*)vchunk;
	addu_buf = (unsigned char *)addu_buffer;
	addv_buf = (unsigned char *)addv_buffer;

	// 对UV块进行上采样
	for (j = 0; j < y_dim/2; j++)
	{
		psu = u + j * x_dim / 2;//原始数据U块指针
		psv = v + j * x_dim / 2;//原始数据V块指针

		pu1 = addu_buf + 2 * j * x_dim;      //U块奇数行首地址
		pu2 = addu_buf + (2 * j + 1) * x_dim;//U块偶数行首地址

		pv1 = addv_buf + 2 * j * x_dim;      //V块奇数行首地址
		pv2 = addv_buf + (2 * j + 1) * x_dim;//V块偶数行首地址

		for (i = 0; i < x_dim/2; i++)
		{
			*pu1 = *psu;
			*pu2 = *psu;
			pu1++;
			pu2++;
			*pu1 = *psu;
			*pu2 = *psu;   //U块上采样:“以1作4”

			*pv1 = *psv;
			*pv2 = *psv;	
			pv1++;
			pv2++;		
			*pv1 = *psv;
			*pv2 = *psv;   //V块上采样:“以1作4”

			pu1++;
			pu2++;

			pv1++;
			pv2++;

			psu++;
			psv++;			
		}
	}	
	// convert YUV to RGB
	if (!flip) {
		for (j = 0; j < y_dim; j++)
		{
			y = y + (y_dim - j - 1) * x_dim;
			addu_buf = addu_buf + (y_dim - j - 1) * x_dim;
			addv_buf = addv_buf + (y_dim - j - 1) * x_dim;

			for (i = 0; i < x_dim; i++) {
				g = b + 1;
				r = b + 2;
				adjust(b, g, r, y, addu_buf, addv_buf);
				b += 3;
				y++;
				addu_buf++;
				addv_buf++;
			}
		}
	}
	else {		
		for (i = 0; i < size; i++)
		{
			g = b + 1;
			r = b + 2;
			adjust(b, g, r, y, addu_buf, addv_buf);//对变量范围作适应性调整
			b += 3;
			y++;
			addu_buf++;
			addv_buf++;
		}
	}
	free(addu_buffer);
	free(addv_buffer);

	return 0;
}
void InitLookupTable()
{
	int i;
	for (i = 16; i < 240; i++) YUV2RGB14075[i] = (float)1.4075 * (i-128);
	for (i = 16; i < 240; i++) YUV2RGB03455[i] = (float)0.3455 * (i - 128);
	for (i = 16; i < 240; i++) YUV2RGB07169[i] = (float)0.7169 * (i - 128);//
	for (i = 16; i < 240; i++) YUV2RGB1779[i] = (float)1.779 * (i-128);
}
void adjust(unsigned char *b, unsigned char *g, unsigned char *r, unsigned char *y, unsigned char *u, unsigned char  *v)
{
	float temp = 0;
	temp = (float)(*y + YUV2RGB1779[*u]);
	temp = temp>255 ? 255 : temp;
	temp = temp<0 ? 0 : temp;
	*b = (unsigned char)temp;

	temp = (float)(*y - YUV2RGB03455[*u] - YUV2RGB07169[*v]);
	temp = temp>255 ? 255 : temp;
	temp = temp<0 ? 0 : temp;
	*g = (unsigned char)temp;

	temp = (float)(*y + YUV2RGB14075[*v]);
	temp = temp>255 ? 255 : temp;
	temp = temp<0 ? 0 : temp;
	*r = (unsigned char)temp;
} 

四、程序分析

RGB格式裸数据部分,三通道的排列顺序依次是B\G\R,所以一个像素点有三个分别表示颜色的数据。

 YUV格式通常有两大类:

打包(packed)格式:将YUV分量存放在同一个数组中,通常是几个相邻的像素组成一个宏像素(macro-pixel);

平面(planar)格式:使用三个数组分开存放YUV三个分量,就像是一个三维平面一样。

下面是我们测试所用RGBYUV文件的两张丑丑的排列简示图~

依次是B G R B G R.........

每帧数据Y U V分块排列......


程序实现过程:

1.  主程序实现对YUV视频逐帧读取数据分别处理,根据格式特点,开辟三个缓存分别存放YUV三个分量各自的数据。

2.  关键函数首先对U\V色差信号作上采样,下采样过程中相邻四个数据取平均得到一个U/V,所以上采样我们这里相应地就可以->以1代4。

3.  接着就是带入公式计算,依然使用查找表的方法,重点注意强制类型转换过程,在void adjust(~~~)函数中实现,超出0~255范围内的rgb值, 负数取0,大于255的取255。

4.  最后按照存储格式写入文件。

五、实验结果

(所有YUV测试文件均来自网站:http://trace.eas.asu.edu/yuv/index.html)

1.  如何检验程序结果是否正确:

由于RGB的裸数据并不构成文件格式,所以我们的YUV视频通过程序转换成RGB文件以后,再用RGB2YUV程序转换成YUV格式文件,用YUV播放 器查看,对比前后两个视频图像画面,若有明显色调失真或不明色块之类则表明程序出错。

2.  下面是这次实验程序编写过程中出现的错误整理:

①程序结果出错:

测试得到上图结果时,首先注意到明显的蓝红色块,猜测是公式计算过程中强制类型转换错误,老师上课也提醒过,查阅相关书籍得到C语言各种数据类型大小范围如下图:

改正: 将公式计算和强制类型转换过程另写函数,将计算所得结果在unsignedchar类型 0~255范围之外的再次处理。

②程序运行报错:program received signal SIGSEGV,Segmentationfault.

通过相关搜索以及不断改正调试发现,出现这种错误的原因是因为声明指针后没有初始化,指针指向地址为未知,未初始化指针直接执行运算就会出错。

改正:程序中声明的指针初始化。

③程序本身错误:对比差错图像与原图像具体差异:

        下面是一组结果图:

蓝红色块错误处理完之后,得到的图像还是不对,但对于错误原因不是很清楚,于是再次进行各种测试。

看上去好像是色调改变了

继续测试


。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

还好找到了这个颜色丰富亮丽的YUV,帮了大忙

似曾相识的竖条色彩分界

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

所以这应该是发生了某部分数据的位置偏移,问题应该出现在有关数据计算部分。

        于是重新查看程序,在直接得到数据的计算过程和U/V色差信号的上采样两部分仔细排查,最终发现错误出现采样程序部分的指针使用。

        错误:程序中指针psu\psv多进行了一次偏移超出数据范围。

上采样部分正确代码已经在上面程序部分给出,指针使用图示如下:

指针简图(V同理):

以上错误改正以后再次进行多次测试,结果表明程序运行结果正确。

test 1----


test 2----


test 3----

五、结论

掌握指针运算很重要,会使程序变的简单。




tips/2017/04/30:对于人眼来说,亮度信号是最敏感的,如果将彩色图像转换为灰度图像,仅仅需要转换保存亮度信号就可以。

 

YUV官方播放器的注册方法:

官方提供注册码:

REGEDIT4

 

[HKEY_CURRENT_USER\Software\tihohod.com\YUVPlayer\RegistrationInfo]

"RegistrationName"="Peace on Earth -Goodwill to Men"

"RegistrationKey"="JxsQHEseR43VXvp6kCV8w5ACtqNVJ11LhOAE4Pztk+Ksw211wohNjsR78XHoGajwlqIOU6gVz0zipwkJCfdvIqyAJrEgJLYB+NKwPGaD+OqbJD+oe+5xixyIKc7tT0ecQjNER47sA3HsUUhRl3LmKozxS2nH233WNPpJwoF4rL1Bjm5OfcM/jiixF/By85wQTdzSwAehjfvB7iO9tCaI9A=="

把上面key的内容复制到一个编辑器,另存为key.reg,点击运行这个文件会自动注册,就OK了





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值