C++实现RGB2YUV/YUV2RGB

一、实验原理

1.内存方式

(1) RGB内存方式

在这里插入图片描述

(2) YUV内存方式

在这里插入图片描述

2.彩色空间转换公式

(1) YUV2RGB的转换公式

由电视原理可知,亮度和色差信号的构成如下:
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) YUV2RGB空间的转换公式

R = Y+(R-Y) = Y+1.14075(V-128)
G = Y+(G-Y) = Y-0.7169(V-128)-0.3455(U-128)
B = Y+(B-Y) = Y+1.779(U-128)

3.码电平分配及数字表达式

(1) 亮电平信号量化后码电平分配:

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

(2) 色差信号量化后码电平分配:

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

4.色度格式

4:2:0格式是指色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半。具体见下图:
在这里插入图片描述

二、实验流程

以YUV2RGB为例:

1.新建工程,创建两个源文件 main.cpp和 yuv2rgb.cpp以及一个头文件 yuv2rgb.h。其中头文件放函数的声明(包括转换函数YUV2RGB和查找表函数InitLookupTable)。YUV2RGB函数负责格式转换。

2.在主函数中声明并初始化变量。

3.读取yuv文件并为其分配内存空间。

4.调用YUV2RGB函数进行格式转换。

5.将转换后的数据写入rgb文件。

6.释放内存,关闭文件。

三、错误及调试过程

错误1:RGB2YUV中生成的yuv文件呈紫色

在这里插入图片描述
调试:确认验证数据已读进buffer后,怀疑是y、u、v的计算处出现问题。打断点单步执行,发现指针y指向的数据始终为0,u、v指向的数据也小于预期。鼠标移至RGBYUV02990等数组处,发现数组内各项为0!!错误原因是没有调用数据表对数组进行初始化。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

错误2:YUV2RGB中生成的rgb文件存在红蓝噪点

左为原rgb转成的yuv图,右为刚刚得到的rgb转成的yuv图

分析:转换公式得到的值为浮点型,强制类型转换会使超过255的值变成0。于是定义三个浮点型变量 rt, gt, bt进行防溢出处理。

修改后结果如下:
在这里插入图片描述

三、代码

RGB2YUV\main.cpp
//RGB2YUV\main.cpp
#include "pch.h"
#include <iostream>
#include "rgb2yuv.h"
#include <malloc.h>

int main(int argc, char* argv[])
{
	//两个文件指针
	FILE* rgbFile = NULL;
	FILE* yuvFile = NULL;

	char* rgbFileName = NULL;
	char* yuvFileName = NULL;
	int Width;
	int Height;
	rgbFileName = argv[1];
	yuvFileName = argv[2];
	Width = atoi(argv[3]);
	Height = atoi(argv[4]);

	//打开文件
	errno_t err;
	if ((err = fopen_s(&rgbFile, rgbFileName, "rb")) == 0)//打开rgb文件
		printf("The file\t%s\twas opened\n", rgbFileName);
	else
		printf("The file\t%s\twas not opened\n", rgbFileName);
	if ((err = fopen_s(&yuvFile, yuvFileName, "wb")) == 0)//打开yuv文件
		printf("The file\t%s\twas opened\n", yuvFileName);
	else
		printf("The file\t%s\twas not opened\n", yuvFileName);

	//四个动态内存区指针
	unsigned char* rgbBuffer = NULL;
	unsigned char* yBuffer = NULL;
	unsigned char* uBuffer = NULL;
	unsigned char* vBuffer = NULL;

	//分配动态内存
	rgbBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height * 3);
	yBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height);
	uBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height / 4);
	vBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height / 4);

	//rgb读出存入动态内存
	if ((fread(rgbBuffer, sizeof(unsigned char), Width*Height * 3, rgbFile)) != 0)
		printf("The file\t%s\twas read\n", rgbFileName);
	else
		printf("The file\t%s\twas not read\n", rgbFileName);

	//调用RGB2YUV函数实现转换
	if ((RGB2YUV(rgbBuffer, yBuffer, uBuffer, vBuffer, Width, Height)) == 0)
		printf("changed successfully");
	else
	{
		printf("fail");
		exit(1);
	}
	
	//设定保护带
	int i;
	for (i = 0; i < Width*Height; i++)
	{
		if (yBuffer[i] <= 15)
			yBuffer[i] = 16;
		if (yBuffer[i] >=236)
			yBuffer[i] =235;
	}
	for (i = 0; i < Width*Height/4; i++)
	{
		if (uBuffer[i] <= 15)
			uBuffer[i] = 16;
		if (uBuffer[i] >= 241)
			uBuffer[i] = 240;
	}
	for (i = 0; i < Width*Height / 4; i++)
	{
		if (vBuffer[i] <= 15)
			vBuffer[i] = 16;
		if (vBuffer[i] >= 241)
			vBuffer[i] = 240;
	}

	//动态内存写入yuv
	fwrite(yBuffer, sizeof(unsigned char), Width*Height , yuvFile);
	fwrite(uBuffer, sizeof(unsigned char), Width*Height/4, yuvFile);
	fwrite(vBuffer, sizeof(unsigned char), Width*Height/4, yuvFile);

	//释放内存,关闭文件
	free(yBuffer);
	free(uBuffer);
	free(vBuffer);
	free(rgbBuffer);

	fclose(rgbFile);
	fclose(yuvFile);
	system("pause");
	return 0;

}
RGB2YUV\rgb2yuv.cpp
//RGB2YUV\rgb2yuv.cpp
#include "pch.h"
#include <iostream>
#include "rgb2yuv.h"


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

int RGB2YUV(void* rgb, void* y_out, void* u_out, void* v_out, int W, int H)
{
	unsigned char *b = NULL, *g = NULL, *r = NULL;
	unsigned char *y, *u, *v;
	b = (unsigned char *)rgb;
	y = (unsigned char *)y_out;
	u = (unsigned char *)u_out;
	v = (unsigned char *)v_out;

	InitLookupTable();//调用数据表

	int i, j;
	//亮度信号转换
	for (i = 0; i < W*H; i++)
	{
		g = b + 1;
		r = b + 2;
		*y = (unsigned char)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
		b += 3;
		y++;

	}

	//色差信号转换
	b = (unsigned char *)rgb;
	for (j = 0; j < H; j++)
	{
		for (i = 0; i < W; i++)
		{
			g = b + 1;
			r = b + 2;
			if (i % 2 == 0 && j % 2 == 0)
			{
				*u = (unsigned char)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
				*v = (unsigned char)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
				u++;
				v++;
			}
			b += 3;
		}
	}
	return  0;
}

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;
}
YUV2RGB\main.cpp
#include "pch.h"
#include <iostream>
#include "yuv2rgb.h"
#include <malloc.h>


int main(int argc, char* argv[])
{
	//两个文件指针
	FILE* yuvFile = NULL;
	FILE* rgbFile = NULL;
	
	char* yuvFileName = NULL;
	char* rgbFileName = NULL;
	int Width;
	int Height;	
	yuvFileName = argv[1];
	rgbFileName = argv[2];

	Width = atoi(argv[3]);
	Height = atoi(argv[4]);

	//打开文件
	errno_t err;	
	if ((err = fopen_s(&yuvFile, yuvFileName, "rb")) == 0)//打开yuv文件
		printf("The file\t%s\twas opened\n", yuvFileName);
	else
		printf("The file\t%s\twas not opened\n", yuvFileName);

	if ((err = fopen_s(&rgbFile, rgbFileName, "wb")) == 0)//打开rgb文件
		printf("The file\t%s\twas opened\n", rgbFileName);
	else
		printf("The file\t%s\twas not opened\n", rgbFileName);

	//四个动态内存区指针	
	unsigned char* yBuffer = NULL;
	unsigned char* uBuffer = NULL;
	unsigned char* vBuffer = NULL;
	unsigned char* rgbBuffer = NULL;

	//分配动态内存	
	yBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height);
	uBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height / 4);
	vBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height / 4);
    rgbBuffer = (unsigned char *)malloc(sizeof(unsigned char)*Width*Height * 3);

	//从三个文件中读出数据 写入动态内存空间yuv
	if(fread(yBuffer, sizeof(unsigned char), Width*Height, yuvFile)&&
		fread(uBuffer, sizeof(unsigned char), Width*Height / 4, yuvFile)&&
		fread(vBuffer, sizeof(unsigned char), Width*Height / 4, yuvFile))
		printf("The file\t%s\twas read\n", rgbFileName);
	else
		printf("The file\t%s\twas not read\n", rgbFileName);


	//调用YUV2RGB函数实现转换
	if ((YUV2RGB( yBuffer, uBuffer, vBuffer,rgbBuffer, Width, Height)) == 0)
		printf("changed successfully");
	else
	{
		printf("fail");
		exit(1);
	}

	//将动态缓冲区数据写入rgb文件
	fwrite(rgbBuffer, sizeof(unsigned char), Width*Height * 3, rgbFile);

	//释放内存,关闭文件
	free(yBuffer);
	free(uBuffer);
	free(vBuffer);
	free(rgbBuffer);

	fclose(rgbFile);
	fclose(yuvFile);
	system("pause");
	return 0;

}
YUV2RGB\yuv2rgb.cpp

```cpp
#include "pch.h"
#include <iostream>
#include "yuv2rgb.h"


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


int YUV2RGB( void* y_in, void* u_in, void* v_in,void* rgb_out, int W, int H)
{

	unsigned char *b,*g,*r,*uBuff,*vBuff;
    unsigned char *y, *u, *v;
    b = (unsigned char *)rgb_out;
	y = (unsigned char *)y_in;
	uBuff = (unsigned char *)u_in;
	vBuff = (unsigned char *)v_in;
	
	u = uBuff;
	v = vBuff;
	InitLookupTable();

	float rt, gt, bt;
	int i, j;
	
	for (j = 0; j < H; j++)
	{
		for (i = 0; i < W; i++)
		{
			g = b + 1;
			r = b + 2;
			rt = (*y) + YUVRGB14075[*v];
			gt = (*y) - YUVRGB07169[*v] - YUVRGB03455[*u];
			bt = (*y) + YUVRGB1779[*u];
			//防溢出
			if (bt < 0)
				bt = 0;
			if (bt > 255)
				bt = 255;

			if (gt < 0)
				gt = 0;
			if (gt > 255)
				gt = 255;

			if (rt < 0)
				rt = 0;
			if (rt > 255)
				rt = 255;
		
			*r = (unsigned char)rt;
			*g = (unsigned char)gt;
			*b = (unsigned char)bt;
			b += 3;

			y++;//y跟着像素走

			//uv采用1/4采样,四个像素一组取每组的左上角
			if (i % 2 == 0 && j % 2 == 0)
			{
				u++;
				v++;
			
			}

			/*u = uBuff + (j / 2)*W / 2 + i / 2;
			v = vBuff + (j / 2)*W / 2 + i / 2;*/
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值