彩色空间转化——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~+0.5之间,要对色差信号进行归一化处理,引入压缩系数后可以得到U、V的计算公式:
    U=-0.1684R-0.3316G+0.5B
    V=0.5R-0.4187G-0.0813B
    此时得到的U、V范围在-128~127之间,为了避免出现负数,加上128。

  • 综合得到RGB2YUV最终的计算公式为:
    Y=0.2990R+0.5870G+0.1140B
    ​U=-0.1684R-0.3316G+0.5B+128
    V=0.5R-0.4187G-0.0813B+128
    反推也可以得到YUV2RGB的公式:
    R=Y+1.4075(V-128)
    G=Y-0.3455(U-128)-0.7169(V-128)
    B=Y+1.7790(U-128)

二、RGB2YUV

  • 编写RGB2YUV的转换函数
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out);
{
	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 *y_buf, *u_buf, *v_buf;
	unsigned char *sub_u_buf, *sub_v_buf;

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

	// 确认图片的宽高为偶数
	if ((x_dim % 2) || (y_dim % 2)) return 1;
	size = x_dim * y_dim;

	// 给指针分配空间
	y_buf = (unsigned char *)y_out;
	sub_u_buf = (unsigned char *)u_out;
	sub_v_buf = (unsigned char *)v_out;
	u_buf = (unsigned char *)malloc(size * sizeof(unsigned char));
	v_buf = (unsigned char *)malloc(size * sizeof(unsigned char));
	if (!(u_buf && v_buf))
	{
		if (u_buf) free(u_buf);
		if (v_buf) free(v_buf);
		return 2;
	}

	b = (unsigned char *)bmp;
	y = y_buf;
	u = u_buf;
	v = v_buf;

	// convert RGB to YUV
	for (j = 0; j < y_dim; j ++)
	{
		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 ++;
			}
		} 

	for (j = 0; j < y_dim; j ++)
	{
		for (i = 0; i < x_dim; i ++)
		{
			y = y_buf + (y_dim - j - 1) * x_dim;
			u = u_buf + (y_dim - j - 1) * x_dim;
			v = v_buf + (y_dim - j - 1) * x_dim;
		}
	}

	// 对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_buf + 2 * j * x_dim;
		pu2 = u_buf + (2 * j + 1) * x_dim;
		pv1 = v_buf + 2 * j * x_dim;
		pv2 = v_buf + (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;
		}
	}

	free(u_buf);
	free(v_buf);

	return 0;
}
  • 为提高运算速度定义查找表
void InitLookupTable()
{

	int i;

	for (i = 0; i < 256; i++) RGBYUV0_2990[i] = (float)0.2990 * i;
	for (i = 0; i < 256; i++) RGBYUV0_5870[i] = (float)0.5870 * i;
	for (i = 0; i < 256; i++) RGBYUV0_1140[i] = (float)0.1140 * i;
	for (i = 0; i < 256; i++) RGBYUV0_1684[i] = (float)0.1684 * i;
	for (i = 0; i < 256; i++) RGBYUV0_3316[i] = (float)0.3316 * i;
	for (i = 0; i < 256; i++) RGBYUV0_4187[i] = (float)0.4187 * i;
	for (i = 0; i < 256; i++) RGBYUV0_0813[i] = (float)0.0813 * i;
}
  • 主函数
int main(int argc, char** argv)
{
	int frameWidth;			
	int frameHeight ;		

	char* yuvFileName = NULL;
	char* rgbFileName = NULL;
	FILE* rgbFile = NULL;
	FILE* yuvFile = NULL;
	unsigned char* rgbBuf = NULL;
	unsigned char* yBuf = NULL;
	unsigned char* uBuf = NULL;
	unsigned char* vBuf = NULL;

	rgbFileName = argv[1];
	yuvFileName = argv[2];

	frameWidth = atoi(argv[3]);
	frameHeight = atoi(argv[4]);
	
	//打开RGB文件
	fopen_s(&rgbFile,rgbFileName,"rb");
	if (rgbFile == NULL)
	{
		printf("cannot find rgb file\n");
		exit(1);
	}
	else
	{
		printf("The input rgb file is %s\n", rgbFileName);
	}

	//打开yuv文件
	fopen_s(&yuvFile,yuvFileName,"wb");
	if (yuvFile == NULL)
	{
		printf("cannot find yuv file\n");
		exit(1);
	}
	else
	{
		printf("The output yuv file is %s\n", yuvFileName);
	}

	//给BUF分配空间
	rgbBuf = (unsigned char*)malloc(frameWidth * frameHeight * 3);

	yBuf = (unsigned char*)malloc(frameWidth * frameHeight);
	uBuf = (unsigned char*)malloc((frameWidth * frameHeight) / 4);
	vBuf = (unsigned char*)malloc((frameWidth * frameHeight) / 4);

	//
	if (rgbBuf == NULL || yBuf == NULL || uBuf == NULL || vBuf == NULL)
	{
		printf("no enought memory\n");
		exit(1);
	}

	//
	fread(rgbBuf, 1, frameWidth * frameHeight * 3, rgbFile);
	
	if(rgbBuf == NULL)
	{
		printf("error");
		return 0;
	}

	//
	RGB2YUV(frameWidth , frameHeight , rgbBuf , yBuf , uBuf ,vBuf);

	//写入yuv文件
	fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
	fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
	fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);

	
	
	//清除
	fclose(rgbFile);
	fclose(yuvFile);

	return(0);
}

  • 运行结果:
    在这里插入图片描述
    在这里插入图片描述
    成功实现了RGB2YUV的转换。

三、YUV2RGB

  • 编写YUV2RGB的转换函数
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 = 0; i < 256; i++) YUVRGB1_4075[i] = (float)1.4075 * i;
	for (i = 0; i < 256; i++) YUVRGB0_3455[i] = (float)0.3455 * i;
	for (i = 0; i < 256; i++) YUVRGB0_7169[i] = (float)0.7169 * i;
	for (i = 0; i < 256; i++) YUVRGB1_7790[i] = (float)1.7790 * i;
}
  • 主函数
int main(int argc, char** argv)
{
	// variables controlable from command line
	int frameWidth = 256;			
	int frameHeight = 256;		
	unsigned int i;
	
	// 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;

	yuvFileName = argv[1];
	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);
	}

	yuvBuf = (unsigned char*)malloc(frameWidth * frameHeight * 3/2);
	rgbBuf = (unsigned char*)malloc(frameWidth * frameHeight * 3);
	yBuf = yuvBuf;
		
	if (yuvBuf == NULL || rgbBuf == NULL || yBuf == NULL)
	{
		printf("no enought memory\n");
		exit(1);
	}

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

	YUV2RGB(frameWidth, frameHeight, yBuf,rgbBuf);
	if (rgbBuf==NULL)
	{
		printf("wrong change\n");
	}
		
	fwrite(rgbBuf, sizeof(unsigned char), frameWidth * frameHeight * 3, rgbFile);
	//printf("\r...%d", ++videoFramesWritten);

	printf("\n%u %ux%u video frames written\n", 
		videoFramesWritten, frameWidth, frameHeight);

	fclose(rgbFile);
	fclose(yuvFile);

	return(0);
}
  • 实验结果截图
    在这里插入图片描述
    在这里插入图片描述
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值