数据压缩【实验一】C++实现YUV与RGB色彩空间的相互转换

一、实验目的

  • 将给定的实验数据用该程序转换为RGB文件。
  • 并与原RGB文件进行比较,如果有误差,分析误差来自何处。
  • 总结 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

(1)RGB2YUV

Y=0.299R+0.587G+0.114B
U=-0.1684R-0.3316G+0.5B+128
V=0.5R-0.4187G-0.0813B+128

(2)YUV2RGB

R=1.0753Y+1.5075(V−128)
G =1.0753Y-0.3441( U −128)-0.6084(V−128)
B = 1.0753Y-0.3441(U-128)-0.6084(V-128)

(3)RGB文件分析

  • 文件中的数据为BGRBGRBGR……
  • R、G、B分别有256*256=65536个数值
  • R、G、B分别据自己前一个数据的偏移量都是3

(4)YUV文件分析

  • 先存放256*256个像素的Y
  • 再存放128*128的U
  • 最后存放128*128的V

(5)量化

量化过程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、实验思路

RGB2YUV

在这里插入图片描述
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip);
x_dim为图像宽度,y_dim为图像高度。
bmp为RGB文件读取数据的指针。
y_out、u_out、v_out为转换后存放Y、U、V数据的输出指针。
flip为是否对图像进行上下颠倒的参数。

通过循环,依次读取每个像素的B、G、R值,并代入公式计算Y、U、V。
对U、V数据进行下采样(取四个点数据均值),以实现128*128的数据量。

YUV2RGB

在这里插入图片描述

四、实验过程

命令行参数设置
在这里插入图片描述

RGB2YUV

(1)代码

程序对于变量数据类型的选择、内存的占用.使得在处理24bits(8B8G8R)的RGB图像时,为其每个像素单个分量开辟的空间都为8bits,几乎不会有任何的浪费,十分精确。


#define u_int8_t	unsigned __int8
#define u_int		unsigned __int32
#define u_int32_t	unsigned __int32
#define FALSE		false
#define TRUE		true

查找表


static float r0299[256],g0587[256],b0114[256],r01684[256],g03316[256],b05[256],r05[256],g04187[256],b00813[256];
void Table()
{
	for(int i=0;i<256;i++) r0299[i]=0.299*i;
	for(int i=0;i<256;i++) g0587[i]=0.587*i;
	for(int i=0;i<256;i++) b0114[i]=0.114*i;
	for(int i=0;i<256;i++) r01684[i]=0.1684*i;
	for(int i=0;i<256;i++) g03316[i]=0.3316*i;
	for(int i=0;i<256;i++) b05[i]=0.5*i;
	for(int i=0;i<256;i++) r05[i]=0.5*i;
	for(int i=0;i<256;i++) g04187[i]=0.4187*i;
	for(int i=0;i<256;i++) b00813[i]=0.0813*i;
}

主函数

int main(int argc,char** argv)
{	
	int height,width;
	char *filename_yuv,*filename_rgb;
	unsigned char *RGB_buffer=NULL;
	unsigned char *YUV_buffer=NULL;
	unsigned char *RGB_buffer_del,*YUV_buffer_del;
	
	filename_yuv=argv[2];
	filename_rgb=argv[1];
	height=atoi(argv[3]);
	width=atoi(argv[4]);
	//读入
	RGB_buffer=new unsigned char[height*width*3];
	YUV_buffer=new unsigned char[height*width*3/2];
	RGB_buffer_del=RGB_buffer;
	YUV_buffer_del=YUV_buffer;
	FILE *fp_yuv=NULL,*fp_rgb=NULL;
	fp_yuv=fopen(filename_yuv,"wb");
	fp_rgb=fopen(filename_rgb,"rb");
	fread(RGB_buffer,sizeof(unsigned char),height*width*3,fp_rgb);
	//RGB2YUV函数
	RGB2YUV(height,width,RGB_buffer,YUV_buffer);
	//写入
	fwrite(YUV_buffer,sizeof(unsigned char),height*width*3/2,fp_yuv);
	delete[] RGB_buffer_del;
	delete[] YUV_buffer_del;
	return 0;
}

由于采用4:2:0的采样空间,需要对U、V进行下采样,这就必须保证x_dim和y_dim均为偶数。

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

RGB2YUV函数

int RGB2YUV(int height,int width,unsigned char *RGB__buffer,unsigned char *YUV__buffer)
{
	unsigned char *b_buffer,*g_buffer,*r_buffer;
	unsigned char *Y_buffer,*U_buffer,*V_buffer;
	unsigned char *u_buffer,*v_buffer;
	unsigned char *pu_up1,*pu_up2,*pv_up1,*pv_up2;
	
	Y_buffer=YUV__buffer;
	U_buffer=YUV__buffer+height*width;
	V_buffer=YUV__buffer+height*width*5/4;
	u_buffer=new unsigned char[height*width];
	v_buffer=new unsigned char[height*width];
	
	pu_up1=u_buffer;
	pu_up2=u_buffer+width;
	pv_up1=v_buffer;
	pv_up2=v_buffer+width;
	b_buffer=RGB__buffer;
	g_buffer=RGB__buffer+1;
	r_buffer=RGB__buffer+2;
	unsigned char *pu_buffer,*pv_buffer;
	pu_buffer=u_buffer;
	pv_buffer=v_buffer;
	
	for(int i=0;i<height;i++)
	{
		for(int j=0;j<width;j++)
		{
			if(r0299[*r_buffer]+g0587[*g_buffer]+b0114[*b_buffer]<0)
				*Y_buffer=(unsigned char)0;
			else
				if(r0299[*r_buffer]+g0587[*g_buffer]+b0114[*b_buffer]>255)
					*Y_buffer=(unsigned char)255;
				else *Y_buffer=unsigned char (r0299[*r_buffer]+g0587[*g_buffer]+b0114[*b_buffer]);
			if(-r01684[*r_buffer]-g03316[*g_buffer]+b05[*b_buffer]<-128)
				*pu_buffer=(unsigned char)0;
			else
				if(-r01684[*r_buffer]-g03316[*g_buffer]+b05[*b_buffer]>128)
					*pu_buffer=(unsigned char)255;
				else *pu_buffer=unsigned char(-r01684[*r_buffer]-g03316[*g_buffer]+b05[*b_buffer]+128);
			if(r05[*r_buffer]-g04187[*g_buffer]-b00813[*b_buffer]<-128)
				*pv_buffer=(unsigned char)0;
			else
				if(r05[*r_buffer]-g04187[*g_buffer]-b00813[*b_buffer]>128)
					*pv_buffer=(unsigned char)255;
				else *pv_buffer=unsigned char(r05[*r_buffer]-g04187[*g_buffer]-b00813[*b_buffer]+128);
			Y_buffer++;
			pu_buffer++;
			pv_buffer++;
			r_buffer+=3;
			g_buffer+=3;
			b_buffer+=3;
		}
	}
	for(int i=0;i<height/2;i++)
	{
		for(int j=0;j<width/2;j++)
		{
			*U_buffer=(*pu_up1+*(pu_up1+1)+*pu_up2+*(pu_up2+1))/4;
			*V_buffer=(*pv_up1+*(pv_up1+1)+*pv_up2+*(pv_up2+1))/4;
			U_buffer++;
			V_buffer++;
			pu_up1+=2;
			pu_up2+=2;
			pv_up1+=2;
			pv_up2+=2;
		}
		if(i!=height/2)
		{
			pu_up1+=width;
			pu_up2+=width;
			pv_up1+=width;
			pv_up2+=width;
		}
	}
	delete[] u_buffer;
	delete[] v_buffer;
	return 0;
}

(2)结果

在这里插入图片描述

YUV2RGB

(1)代码

主函数

int main(int argc, char** argv)
{
	int height,width;
	char *filename_yuv,*filename_rgb;
	filename_yuv=argv[1];
	filename_rgb=argv[2];
	height=atoi(argv[3]);
	width=atoi(argv[4]);
	unsigned char *YUV_buffer=NULL,*Y_buffer,*U_buffer,*V_buffer;
	unsigned char *RGB_buffer=NULL;
	unsigned char *YUV_buffer_del,*RGB_buffer_del;
	YUV_buffer=new unsigned char[height*width*3/2];
	YUV_buffer_del=YUV_buffer;
	if((width%2==1)||(height%2==1))
	{
		printf("please input the right height and width/n");
	}
	if(YUV_buffer==NULL)
	{
		printf("can not new unsignde char/n");
		exit(1);
	}
	FILE *fp_yuv=NULL,*fp_rgb=NULL;
	fp_yuv=fopen(filename_yuv,"rb");
	fp_rgb=fopen(filename_rgb,"wb");
	if((fp_yuv==NULL)||(fp_rgb==NULL))
	{
		printf("can not open the file/n");
		exit(1);
	}
	fread(YUV_buffer,sizeof(unsigned char),height*width*3/2,fp_yuv);
	Y_buffer=YUV_buffer;
	U_buffer=YUV_buffer+height*width;
	V_buffer=YUV_buffer+(height*width*5)/4;
	RGB_buffer=new unsigned char[height*width*3];
	RGB_buffer_del=RGB_buffer;
	if(RGB_buffer==NULL)
	{
		printf("can not new unsigned char/n");
		exit(1);
	}
	YUV2RGB(height,width,Y_buffer,U_buffer,V_buffer,RGB_buffer);
	fwrite(RGB_buffer,sizeof(unsigned char),height*width*3,fp_rgb);
	fclose(fp_yuv);
	fclose(fp_rgb);
	delete[] YUV_buffer_del;
	delete[] RGB_buffer_del;
	return 0;
}

函数

int YUV2RGB(int height,int width,unsigned char *Y__buffer,unsigned char *U__buffer,unsigned char *V__buffer,unsigned char *RGB__buffer)
{
	//查找表
	float v14075[256],u03455[256],v07169[256],u1779[256],y[256];
	for(int i=0;i<256;i++) v14075[i]=(i-128)*1.4075;
	for(int i=0;i<256;i++) u03455[i]=(i-128)*0.3455;
	for(int i=0;i<256;i++) v07169[i]=(i-128)*0.7169;
	for(int i=0;i<256;i++) u1779[i]=(i-128)*1.779;
	for(int i=0;i<256;i++) y[i]=i;
	
	unsigned char *u_buffer,*v_buffer;
	unsigned char *pu_up1,*pu_up2,*pv_up1,*pv_up2;
	unsigned char *b_buffer,*g_buffer,*r_buffer;
	unsigned char *u_buffer_del,*v_buffer_del;
	
	u_buffer=new unsigned char[height*width];
	v_buffer=new unsigned char[height*width];
	u_buffer_del=u_buffer;
	v_buffer_del=v_buffer;
	pu_up1=u_buffer;
	pu_up2=u_buffer+width;
	pv_up1=v_buffer;
	pv_up2=v_buffer+width;
	
	for(int i=0;i<(height/2);i++)
	{
		for(int j=0;j<(width/2);j++)
		{
			*pu_up1=*U__buffer;
			*(pu_up1+1)=*U__buffer;
			*pu_up2=*U__buffer;
			*(pu_up2+1)=*U__buffer;
			pu_up1+=2;
			pu_up2+=2;
 
			*pv_up1=*V__buffer;
			*(pv_up1+1)=*V__buffer;
			*pv_up2=*V__buffer;
			*(pv_up2+1)=*V__buffer;
			pv_up1+=2;
			pv_up2+=2;
 
			U__buffer++;
			V__buffer++;
 
		}
		if(i!=(height/2))
		{
			pu_up1+=width;
			pu_up2+=width;
			pv_up1+=width;
			pv_up2+=width;
		}
	}
	b_buffer=RGB__buffer;
	g_buffer=RGB__buffer+1;
	r_buffer=RGB__buffer+2;
	for(int i=0;i<(height*width);i++)//计算rgb
	{
		
		if((y[*Y__buffer]+v14075[*v_buffer])<0)
			*r_buffer=(unsigned char)0;
		else
			if((y[*Y__buffer]+v14075[*v_buffer])>255)
				*r_buffer=(unsigned char)255;
			else *r_buffer=(unsigned char)(y[*Y__buffer]+v14075[*v_buffer]);
		
		if((y[*Y__buffer]-u03455[*u_buffer]-v07169[*v_buffer])<0)
			*g_buffer=(unsigned char)0;
		else
			if((y[*Y__buffer]-u03455[*u_buffer]-v07169[*v_buffer])>255)
				*g_buffer=(unsigned char)255;
			else *g_buffer=(unsigned char)(y[*Y__buffer]-u03455[*u_buffer]-v07169[*v_buffer]);
 
		if((y[*Y__buffer]+u1779[*u_buffer])<0)
			*b_buffer=(unsigned char)0;
		else
			if((y[*Y__buffer]+u1779[*u_buffer])>255)
				*b_buffer=(unsigned char)255;
			else *b_buffer=(unsigned char)(y[*Y__buffer]+u1779[*u_buffer]);
		b_buffer+=3;
		g_buffer+=3;
		r_buffer+=3;
		Y__buffer++;
		u_buffer++;
		v_buffer++;
	}
	delete[] u_buffer_del;
	delete[] v_buffer_del;
	return 0;
}

(2) 结果

在这里插入图片描述

五、实验分析

分量
B6.85686
G7.17846
R7.22955
分量
Y6.331819
U5.126402
V4.113143

(1)YUV为了便于传输,U、V分量只有原本的1/4。对于原本色度变化缓慢的图像区域,这种下采样造成的影响不大;但对于色度急剧变化的区域,下采样就导致色度信息被丢失了。因此转换后的RGB文件分布曲线的抖动更大了,变化不那么平滑了。
(2)YUV与RGB色彩空间并不重合,在色彩空间变换时存在溢出。
(3)对U、V数据进行上采样时,选择直接将值进行4次复制,引入了一定的误差。
(4)查找表的思想,以空间换时间,当需要处理大量文件时,会产生更大的优势。
(5)图像数据的存储存在冗余信息,由于人眼感知特性的灵敏度有限,对于部分数据可以进行压缩,以减小传输数据量。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
YUV422转换为RAW有多种方法,其中一种常用的方法是将YUV422数据压缩RGB数据,再将RGB数据重新编码为RAW格式。下面是一个示例代码实现: ```c++ #include <iostream> #include <fstream> using namespace std; void YUV422toRGB(unsigned char y, unsigned char u, unsigned char v, unsigned char &r, unsigned char &g, unsigned char &b) { int r1, g1, b1; int c = y - 16, d = u - 128, e = v - 128; r1 = (298 * c + 409 * e + 128) >> 8; g1 = (298 * c - 100 * d - 208 * e + 128) >> 8; b1 = (298 * c + 516 * d + 128) >> 8; r = (r1 < 0) ? 0 : ((r1 > 255) ? 255 : r1); g = (g1 < 0) ? 0 : ((g1 > 255) ? 255 : g1); b = (b1 < 0) ? 0 : ((b1 > 255) ? 255 : b1); } int main() { ifstream input("input.yuv", ios::binary); ofstream output("output.raw", ios::binary); unsigned char y, u, v, r, g, b; int width = 640, height = 480; int y_size = width * height; int uv_size = y_size / 2; while (input.read(reinterpret_cast<char*>(&y), 1)) { input.read(reinterpret_cast<char*>(&u), 1); input.read(reinterpret_cast<char*>(&v), 1); YUV422toRGB(y, u, v, r, g, b); output.write(reinterpret_cast<char*>(&r), 1); output.write(reinterpret_cast<char*>(&g), 1); output.write(reinterpret_cast<char*>(&b), 1); } input.close(); output.close(); return 0; } ``` 该代码将从名为“input.yuv”的二进制文件中读取YUV422数据,并将每个像素的YUV转换RGB值。 然后,将RGB数据写入名为“output.raw”的二进制文件中。由于RAW格式可以是任何未压缩的像素格式,因此可以根据需要更改输出文件的扩展名和格式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值