数据压缩3 | 实验1 | 彩色空间转换 RGB<=>YUV

一、内容拆解

(1) 编写RGB转化为YUV程序,重点掌握函数定义部分查找表的初始化和调用,缓冲区分配。将得到的RGB文转换为YUV文件,用YUV Viewer播放器观看,验证是否正确。

(2) 编写将YUV转换为RGB的程序。将给定的实验数据用该程序转换为RGB文件。并与原RGB文件进行比较,如果有误差,分析误差来自何处。

==>
两次转换 => RGB与YUV420存储方式,转换公式
代码实现=>部分查找表是什么,怎么用?
分析误差=>误差怎么呈现,什么原因

二、思路概述

1. RGB->YUV

a. 读取rgb文件,新建yuv文件;
b. 开辟缓存区,rgb文件以RGB开辟一个3倍hw的数组,第0位B,第1位G,第2位B,第3位B……;yuv文件存储形式420,开辟一个hw存Y,两个h*w/4分别存U、V;
c. 根据公式建立部分查找表;
d. 调用部分查找表,每一组rgb确定一个Y,每偶数行偶数列rgb确定一个U和V,确保三者比例为4:2:0;
e. YUV取值范围限制,避免溢出;
f. 导出数据存入新建的yuv文件。

2. YUV->RGB

a. 读取上一yuv文件,新建rgb文件;
b. 开辟缓存区,rgb文件以RGB开辟一个3倍hw的数组,第0位B,第1位G,第2位B,第3位B……;yuv文件存储形式420,开辟一个hw存Y,两个h*w/4分别存U、V;
c. 根据公式建立部分查找表;
d. 调用部分查找表,每4个Y、1个U、1个V确定4个R、G、B;
e. RGB取值范围限制,避免溢出;
f. 导出数据存入新建的rgb文件。
g. 用Python 作图比较两个rgb文件,调整代码,分析误差。

三、关键代码

1. 查表法

(1) 好处
对于本实验,将一些事先计算好的结果存储在常量数组中,节省计算开销。
(2) 本实验用的是部分查找表,但是我也看到一篇博客说可以尝试完全查找表:

G=Y–0.343×(U–128)–0.714×(V–128) =>> G =
yig2g_table[y][uv2ig_table[u][v],而RB本身就只和YU或YV相关,所以这样我们一共需要4个8*8的二维表格,需要占用4乘2的16次方共256K内存.

看上去就很难,我应该不会写

RGB2YUV

float rgb2yuv02990[256], rgb2yuv05870[256], rgb2yuv01140[256], rgb2yuv01684[256],
rgb2yuv03316[256], rgb2yuv05000[256], rgb2yuv04187[256], rgb2yuv00813[256];
void Initrgb_table() {
	for (int i = 0; i < 256; i++)
	{
		rgb2yuv02990[i] = (float)0.299 * i;
		rgb2yuv05870[i] = (float)0.587 * i;
		rgb2yuv01140[i] = (float)0.114 * i;
		rgb2yuv01684[i] = (float)0.1684 * i;
		rgb2yuv03316[i] = (float)0.3316 * i;
		rgb2yuv05000[i] = (float)0.5 * i;
		rgb2yuv04187[i] = (float)0.4187 * i;
		rgb2yuv00813[i] = (float)0.0813 * i;
	}
}

YUV2RGB

float yuv2rgb14030[256], yuv2rgb03430[256], yuv2rgb07140[256], yuv2rgb1770[256];
void Inityuv_table() 
{
	for (int i = 0; i < 256; i++)
	{
		yuv2rgb14030[i] = (float)1.4030 * (i - 128);
		yuv2rgb03430[i] = (float)0.3430 * (i - 128);
		yuv2rgb07140[i] = (float)0.7140 * (i - 128);
		yuv2rgb1770[i] = (float)1.770 * (i - 128);
	}
}

2. RGB->YUV

(1)开辟缓存区,YUV2RGB类似,不赘述

	int Width = 256, Height = 256;
	unsigned char* RGB;
	unsigned char* Y, * U, * V;
	unsigned char* R, * G, * B;
	RGB = (unsigned char*)malloc(sizeof(char) * (Width * Height * 3));
	R = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height));
	G = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height));
	B = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height));
	Y = (unsigned char*)malloc(sizeof(char) * (Width * Height));
	U = (unsigned char*)malloc(sizeof(char) * (Width * Height / 4));
	V = (unsigned char*)malloc(sizeof(char) * (Width * Height / 4));

(2) 调用部分查找表,转换得到yuv数据

int y = 0;
	for (int i = 0; i < Width * Height * 3; i = i + 3)
	{
		Initrgb_table();
		Y[y] = rgb2yuv01140[RGB[i]] + rgb2yuv05870[RGB[i + 1]] + rgb2yuv02990[RGB[i + 2]];
		//限制Y分量范围
		if (Y[y] > 235)
			Y[y] = 235;
		if (Y[y] < 16)
			Y[y] = 16;
		y++;
	}

UV略微复杂,在这里我看了很多博客,代码复现都或多或少有点问题,姑且一看

	unsigned char* b = NULL;
	unsigned char* g = NULL;
	unsigned char* r = NULL;
	unsigned char* u = NULL;
	unsigned char* v = NULL;

	b = (unsigned char*)RGB;
	u = (unsigned char*)U;
	v = (unsigned char*)V;
	int m = 0, n = 0;
	for (int  j = 0; j < Height; j++)
	{
		for (int i = 0; i < Width; i++)
		{
			g = b + 1;
			r = b + 2;
			if (i%2 == 0 && j%2 ==0)
			{
				Initrgb_table();
				*u = (unsigned char)(rgb2yuv05000[*b] - rgb2yuv03316[*g] - rgb2yuv01684[*r] + 128);
				*v = (unsigned char)(-rgb2yuv00813[*b] - rgb2yuv04187[*g] + rgb2yuv05000[*r] + 128);
				u++;
				v++;
			}
			b += 3;
		}
	}

没有加限制,unsigned char*类型进行范围限制,图片偏绿,如果没有限制,效果反而还可以。

在这里插入图片描述

在这里插入图片描述
为了最终效果,我非常果断地没加……

3. YUV->RGB

(1) 调用部分查找表,转换得到rgb数据

void yuv2rgb(unsigned char* R, unsigned char* G, unsigned char* B, unsigned char* Y, unsigned char* U, unsigned char* V, int Width, int Height)
{
	int j = 0;
	for (int i = 0; i < Width * Height;)
	{
		Inityuv_table();
		R[i] = Y[i] + yuv2rgb14030[V[j]];
		R[i + 1] = Y[i + 1] + yuv2rgb14030[V[j]];
		R[i + Width] = Y[i + Width] + yuv2rgb14030[V[j]];
		R[i + Width + 1] = Y[i + Width + 1] + yuv2rgb14030[V[j]];

		G[i] = Y[i] - yuv2rgb03430[U[j]] - yuv2rgb07140[V[j]];
		G[i + 1] = Y[i + 1] - yuv2rgb03430[U[j]] - yuv2rgb07140[V[j]];
		G[i + Width] = Y[i + Width] - yuv2rgb03430[U[j]] - yuv2rgb07140[V[j]];
		G[i + Width + 1] = Y[i + Width + 1] - yuv2rgb03430[U[j]] - yuv2rgb07140[V[j]];

		B[i] = Y[i] + yuv2rgb1770[U[j]];
		B[i + 1] = Y[i + 1] + yuv2rgb1770[U[j]];
		B[i + Width] = Y[i + Width] + yuv2rgb1770[U[j]];
		B[i + Width + 1] = Y[i + Width + 1] + yuv2rgb1770[U[j]];

		//限制RGB范围
		if (R[i] < 0)
			R[i] = 0;
		if (R[i] > 255)
			R[i] = 255;
		if (G[i] < 0)
			G[i] = 0;
		if (G[i] > 255)
			G[i] = 255;
		if (B[i] < 0)
			B[i] = 0;
		if (B[i] > 255)
			B[i] = 255;
		if (i % 256 == 254)
			i = i + 256 + 2;
		else
			i = i + 2;
		j++;
	}
}

(2) 分析两个rgb文件存在的误差(还是用上次的Jupyter代码画图)
在这里插入图片描述 在这里插入图片描述
比较可以看出,在0-25处,R的波动最大,这与图像中大面积绿、蓝色块有关,其他区域RGB曲线的更毛躁,这与YUV复现时缺少部分UV分量数据造成像素值跳跃有关,整体来看,rgb复现成果较好。

四、总结吐槽

  1. 从前辈博客看到的:一个既能播放RGB也能播放YUV的播放器,分享:http://wwww.236.6122.net/uploadFile/2013/vooya.rar。
  2. 上采样放大图像(或称为上采样(upsampling)或图像插值(interpolating))的主要目的是放大原图像,从而可以显示在更高分辨率的显示设备上。下采样缩小图像(或称为下采样(subsampled)或降采样(downsampled))的主要目的有两个:①使得图像符合显示区域的大小;②生成对应图像的缩略图。在实验中表现为,下采样我们用偶数行偶数列的RGB进行转换UV,上采样我们用4个Y、1个U、1个V来转换RGB,实际上就是将420转换为444;
  3. 转换公式问题,真的是看了很多博客,包括有总结类博客,但是依旧有问题,这里简单列举一下:
    第一种这里的公式rgb取值范围为[0,1],y范围[0,1],uv范围[-0.5,0.5]
    Y=0.2990R+0.5870G+0.1140B
    R-Y=0.7010R-0.5870G-0.1140B
    B-Y=-0.2990R-0.5870G+0.8860B
    U=-0.1684R-0.3316G+0.5B
    V=0.5R-0.4187G-0.0813B
    第二种这里的公式rgb、yuv取值范围都为[0,255],防止溢出,需要加范围限制
    R=Y+1.403×(V−128)
    G=Y–0.343×(U–128)–0.714×(V–128)
    B=Y+1.770×(U–128)
    Y=0.2990R+0.5870G+0.1140B
    U=-0.1684R-0.3316G+0.5B+128
    V=0.5R-0.4187G-0.0813B+128
  4. 不要拖延,事情本来就很多,一拖什么都做不了。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月婵婵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值