实验一:彩色空间转换实验——YUV2RGB

一、实验原理

R、G、B三基色信号转化成Y、R-Y、B-Y的公式如下:

Y=0.2990R+0.5870G+0.1140B

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

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

(一)RGB转换为YUV的公式

为使R-Y、B-Y的信号动态范围控制在0.5之间,对色差信号进行归一化,引入了幅度压缩系数,得到模拟视频分量CR、CB ,用V表示CR,U表示CB

CR=0.7130(R-Y)       CB=0.5640(B-Y)

V=0.5000R-0.4187G-0.0813B

U=-0.1684R-0.3316G+0.5000B

为了便于数字化处理,再将CR、CB引入128的偏置,得到的C'R、C'B也用V和U表示:

V=0.7130(R-Y)+128

U=0.5640(B-Y)+128

(二)YUV转换为RGB的公式

将YUV转换成RGB时,根据上式可以得出运算公式:

R=Y+1.4025*(V-128)

G=Y-0.7143*(V-128)-0.3443*(U-128)

B=Y+1.7730*(U-128)

(三)取样格式及空间分配

取样格式是4:2:0,意味着色差信号V、U的取样频率是亮度信号Y的四分之一,水平方向和垂直方向的点数均为Y的一半。所以RGB转换为YUV时进行下采样,即在包含4个样点的2×2的方格中,计算得到4个Y值,并对4个RGB值进行V和U的运算,再求均值得到最终值。YUV转换为RGB时要进行上采样,将U、V恢复成与Y相同的个数,再进行运算。所以在一帧图像中,给RGB开辟的空间大小应为(宽×高×3)字节,给Y开辟的空间大小应为(宽×高)字节,给U和V分别开辟的空间大小应为(宽×高×0.25)字节。(默认量化比特数为8bit,即每个像素占一字节)

二、实验的流程分析

1.先读入待转换的文件名、宽高以及输出文件名。

2.在程序开始设置初始化参数:定义宽高等变量,用malloc函数为缓冲区开内存,注意buffer是无符号整型。

3.打开两个文件,给变量赋值,读数据到y_Buffer、u_Buffer、v_Buffer中,并从中取数据逐步计算,结果放入rgb_Buffer中。

4.输出结果到外部文件中,用YUVviewer验证是否正确。

5.程序编译成功后,未运行前,点击工程->设置->调试,需要输入工作目录和程序变量,否则会因找不到图片而得不到结果。


6.用YUVviewer验证结果时,图像尺寸别忘记选择,否则不能出现正确图像(如左下图)。



三、实验的相关代码

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);//函数参数要保持一致

void InitLookupTable();

#endif

yuv2rgb.cpp

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

static float YUVRGB14025[256], YUVRGB07143[256], YUVRGB03443[256],YUVRGB17730[256];

int YUV2RGB (int x_dim,int y_dim,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, *rgb;
	unsigned char *y, *u, *v , *up_u, *up_v;
	unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;//定义相关的指针
	unsigned char *y_buffer, *u_buffer, *v_buffer,*rgb_buffer;//定义原rgb,y,u,v的缓冲空间
	unsigned char *up_u_buffer, *up_v_buffer;//定义上采样后的u,v的缓冲空间
    float r_,g_,b_;//定义中间变量,判断强制类型转换后是否越界
	
	if (init_done == 0)
	{
		InitLookupTable();
		init_done = 1;
	}//初始化查找表

	if ((x_dim % 2) || (y_dim % 2)) return 1;
	size = x_dim * y_dim;

	rgb_buffer = (unsigned char *)rgb_out;
	y_buffer = (unsigned char *)y_in;
	u_buffer = (unsigned char *)u_in;
	v_buffer = (unsigned char *)v_in;
	up_u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
	up_v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));//为y,u,v,rgb,up_u,up_v开辟缓冲空间

	if (up_u_buffer == NULL || up_v_buffer == NULL)  return 2;

	rgb = rgb_buffer;
	y = y_buffer;
	u = u_buffer;
	v = v_buffer;
	up_u = up_u_buffer;
	up_v = up_v_buffer;

	//对u,v上采样
	for (j = 0; j < y_dim/2; j ++)
	{
		psu = u + j*x_dim/2;
        psv = v + j*x_dim/2;
        pu1 = up_u + 2 * j * x_dim;//pu1指向偶数行
        pu2 = up_u + (2 * j + 1)*x_dim;//pu2指向奇数行
        pv1 = up_v + 2 * j * x_dim;
        pv2 = up_v + (2 * j + 1)*x_dim;
        for (i = 0; i < x_dim/2; i ++)
		{
			*pu1 = *psu ;
			*(pu1 + 1) = *psu;
			*pu2 = *psu;
			*(pu2 + 1) = *psu;
            *pv1 = *psv ;
			*(pv1 + 1) = *psv;
			*pv1 = *psv;
			*(pv2 + 1) = *psv;
            psu++;
            psv++;
            pu1 += 2;
            pu2 += 2;
            pv1 += 2;
            pv2 += 2;
		}
	}


	//YUV转换为RGB
	for(j = 0; j< y_dim;j++)
	{
		for (i = 0; i < x_dim; i ++)
		{ 
			b = rgb;
			g = rgb+ 1;
            r = rgb + 2;//一定注意:RGB图像按B、G、R顺序排列
			r_ = (*y + YUVRGB14025[*v]);//r
            g_ = (*y - YUVRGB03443[*u] - YUVRGB07143[*v]);//g
            b_ = (*y + YUVRGB17730[*u]);//b

			
			//对超出范围的值进行处理
            *b=(b_<0?0:(b_>255?255:(unsigned char)b_));
            *g=(g_<0?0:(g_>255?255:(unsigned char)g_));
			*r=(r_<0?0:(r_>255?255:(unsigned char)r_));
		
            rgb += 3;
            y++;
            up_u++;
            up_v++;
			
		}
	}


	if (up_u_buffer != NULL)  
	{
		free(up_u_buffer);
	}
    if (up_v_buffer != NULL)  
	{
		free(up_v_buffer);
	}//释放缓冲区,其他缓冲区前面已释放,不要重复释放
	return 0;
}



void InitLookupTable()
{
	int i;
	for(i=0;i<256;i++) YUVRGB14025[i]=(float)1.4025*(i-128);
	for(i=0;i<256;i++) YUVRGB07143[i]=(float)0.7143*(i-128);
	for(i=0;i<256;i++) YUVRGB03443[i]=(float)0.3443*(i-128);
	for(i=0;i<256;i++) YUVRGB17730[i]=(float)1.7730*(i-128);
} //查找表的函数体,方便计算

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

int main(int argc,char** argv)
{
	u_int frameWidth = 352;
	u_int frameHeight = 240;

	char* yuvFileName = NULL;
	char* rgbFileName = NULL;
	FILE* yuvFile = NULL;
	FILE* rgbFile = NULL;
	u_int8_t* yBuf = NULL;
	u_int8_t* uBuf = NULL;
	u_int8_t* vBuf = NULL;
	u_int8_t* rgbBuf = NULL;//开4个缓冲区,分别存yuv和rgb
	u_int32_t videoFramesWritten = 0;

	yuvFileName = argv[1];
	rgbFileName = argv[2];
	frameWidth = atoi(argv[3]);//atoi是转整型
	frameHeight = atoi(argv[4]);

	/*打开yuv文件*/
	yuvFile = fopen(yuvFileName,"rb");//注意这里的打开模式是读“rb”
	if(yuvFile == NULL)
	{
		printf("Cannot find yuv file!\n");
		exit(1);
	}
	else
	{
		printf("The input yuv file is %s\n",yuvFileName);
	}
	/*打开rgb文件*/
	rgbFile = fopen(rgbFileName,"wb");//这里的打开模式是写“wb”
	if(rgbFile == NULL)
	{
		printf("Cannot find rgb file!\n");
		exit(1);
	}
	else
	{
		printf("The output rgb file is %s\n",rgbFileName);
	}

	/* 开辟缓冲区 */
	yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
	uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
	vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
    rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3 );


	/*判断缓冲区是否存在*/
    if (yBuf == NULL || uBuf == NULL || vBuf == NULL || rgbBuf == 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))
		{
			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);
	
	/*释放缓冲区,关闭文件*/
	if(rgbFile!=NULL)   fclose(rgbFile);
    if(yuvFile!=NULL)   fclose(yuvFile);
    if(rgbBuf!=NULL) free(rgbBuf);
    if(yBuf!=NULL)    free(yBuf);
    if(uBuf!=NULL)   free(uBuf);
    if(vBuf!=NULL)   free(vBuf);

	return(0);

}

在实验过程中,遇到了一些问题。

1.强制类型转换时,如果超出0-255的范围,图像可能会出现噪点。



对超出范围的值进行了处理,原始代码如下:

g = b + 1;
            r = b + 2;
			r_ = (*y + YUVRGB14025[*v]);//r
            g_ = (*y - YUVRGB03443[*u] - YUVRGB07143[*v]);//g
            b_ = (*y + YUVRGB17730[*u]);//b
        
			//对超出范围值的处理
            if (b_ < 0)   (unsigned char *)b = 0;
            if (b_ > 255)  (unsigned char *)b = 255;
            if (g_ < 0)  (unsigned char *)g = 0;
            if (g_ > 255)  (unsigned char *)g = 255;
            if (r_ < 0)   (unsigned char *)r = 0;
            if (r_  > 255)   (unsigned char *)r = 255;

出错:error C2440: '=' :cannot convert from 'const int' to 'unsignedchar*'

这里的r,g,b都是常量整型,不能给它强制转换成无符号的指针。

所以修改了一下:

b = rgb;
			g = rgb+ 1;
            r = rgb + 2;
			r_ = (unsigned char)(*y + YUVRGB14025[*v]);//r
            g_ = (unsigned char)(*y - YUVRGB03443[*u] - YUVRGB07143[*v]);//g
            b_ = (unsigned char)(*y + YUVRGB17730[*u]);//b
        
			//对超出范围值的处理
            if (b_ < 0)   b_= 0;
            if (b_ > 255)  b_= 255;
            if (g_ < 0)  g_= 0;
            if (g_ > 255)  g_= 255;
            if (r_ < 0)   r_= 0;
            if (r_  > 255)   r_= 255;

发现虽然编译没问题了,但在计算的时候就进行强制类型转换,很容易丢掉一些数据,所以对代码继续进行改进,并优化了一些语句,使其更简洁明了。

			g = b+ 1;
            r = b + 2;//一定注意:RGB图像按B、G、R顺序排列
			r_ = (*y + YUVRGB14025[*v]);//r
            g_ = (*y - YUVRGB03443[*u] - YUVRGB07143[*v]);//g
            b_ = (*y + YUVRGB17730[*u]);//b
        
			//对超出范围的值进行处理,要注意强制类型转换的对象
            *b=(b_<0?0:(b_>255?255:(unsigned char)b_));
            *g=(g_<0?0:(g_>255?255:(unsigned char)g_));
			*r=(r_<0?0:(r_>255?255:(unsigned char)r_));

2.编译成功后,运行的时候程序发生中断。在VisualStudio环境下进行调试,F9打断点后,显示76CF919F处push有问题。


EBP在这里表示的是pu1栈帧的底部,它的值一直报错。经过查看pu1的算法,发现给up_u_buffer缓冲区定义错误,将它和u_buffer的定义混淆了。

错误定义是:

up_u_buffer = (unsigned char *)u_in;

u_buffer = (unsigned char *)malloc(size*sizeof(unsigned char));

两者交换后,得到正确代码:

u_buffer = (unsigned char *)u_in;

up_u_buffer = (unsigned char *)malloc(size*sizeof(unsigned char));


四、实验结果及分析

左图down.yuv是从down.rgb转化而来,中图test.yuv经历了down.yuv->test.rgb->test.yuv,右图是二者的差值图像。

两个图像有色差,是因为rgb转yuv进行下采样时丢掉了一部分数据,yuv再转rgb时上采样,数据已有一些损失。


以下三个例子,左边是原图YUV,右边是转换后的YUV。




五、结论

1.malloc和free是一对函数,为指针开辟和释放内存。开辟后不要忘记释放,也不能重复释放,在释放前用if语句进行判断,否则程序可能会中断运行。fopen和fclose用法也是这样。

2.指针作为函数的参数传递,其作用是可以改变指针指向的区域变量。

3.强制类型转换时,如果对超出范围的值不进行处理,图像可能会出现噪点。

4.给程序查错的时候,在Visual Studio的调试环境下先找到出错的地方,F9打断点,一步一步运行。如果是push出了问题,很可能是指针所指区域不正确,根据前后代码的对应关系进行修改。检查代码的时候一定要细心,有时是因为漏写或错写了某个符号,就会导致结果出错。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值