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

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

实验目的 :
1.学会从计算和程序的角度分析问题
通过完成本实验,理解计算思维,即从问题出发,通过逐步分析和分解,把原问题转化为可用程序方式解决的问题。在此过程中设计出一个解决方案。
2.进一步理解彩色空间的概念并掌握不同彩色空间转换的基本方程。
3.通过逐步运行程序,掌握编程细节:如查找表的设计,内存分配,对U和V信号进行下采样,文件读写过程等。掌握程序调试的基本方法。
具体思路:
1.首先从源头理解来,本实验可以认为是由两大步组成:
第一步:将给的down.rgb转化为对应的yuv文件,再用yuv viewer显示
第二步:再将得到的yuv文件,转换回去,重新化为rgb文件,再用pyuv显示,对比转化前后的区别
所以,从思路而言,我们便知道,解决这个问题就一定要用到电视原理里所学过的RGB和YUV之间的转换公式:
即RGB到YUV:

Y=0.2990R+0.5870G+0.1140B
U=−0.1684R−0.3316G+0.5B
V = 0.5R - 0.4187G - 0.0813B

但是在本实验中,对分量信号进行8比特量化时,共分为256个等间隔的量化级。为了防止信号变动造成过载,在256级上端留20级,下端留16级作为信号超越动态范围的保护带,即色差信号零电平对应码电平128,则由RGB到YUV转换后的实际色差信号,即,我们在本实验中的转化公式为:

Y=0.2990R+0.5870G+0.1140B
U=−0.1684R−0.3316G+0.5B+128
V = 0.5R - 0.4187G - 0.0813B+128

而同时,也要注意在代码中计算得出的每一个点的U/V的大小都应控制在16~235的区间内,以防止信号变动造成过载。

而在转换的这个过程中,还需要注意的一点在于:
我们需要转化为的YUV文件是4:2:0格式的,也就是说,转化后的YUV文件垂直水平分解力都是原来RGB文件的1/2。即新的YUV文件中,亮度的取样点数仍然是256256,但U和V两个色度分量的取样点数都下降为了128128,在新的形式之下,我们可以近似的认为,可以按照如下图所示的一组来进行分析:在这里插入图片描述
也就是说,在上图中,圆形的点即为新图像的亮度点,而每四个亮度点共用一组色度信号。故在计算每个色度点取值的时候,我们需要用到原本RGB图像中的该位置处四点的值。此处可由此代码理解:

int u = 0;
	for (int i = 0; i < w * h;)//caculate u
	{
		U_BUFFER[u] = (RED_BUFFER[i] + RED_BUFFER[i + 1] + RED_BUFFER[i + w] + RED_BUFFER[i + w + 1]) / 4 * (-0.1684) +
			          (GREEN_BUFFER[i] + GREEN_BUFFER[i + 1] + GREEN_BUFFER[i + w] + GREEN_BUFFER[i + w + 1]) / 4 * (-0.3316) +
			          (BLUE_BUFFER[i] + BLUE_BUFFER[i + 1] + BLUE_BUFFER[i + w] + BLUE_BUFFER[i + w + 1]) / 4 * (0.500) + 128;
		if (U_BUFFER[u] > 235)
		{
			U_BUFFER[u] = 235;
		}
		if (U_BUFFER[u] < 16)
		{
			U_BUFFER[u] = 16;
		}
		if (i % 256 == 254)
		{
			i = i + 258;
		}
		else
		{
			i = i + 2;
		}
		u++;
	}

2.在得知了如何进行计算之后,其他的操作其实大体和上一次的实验差不多:
第一步:程序的初始化,打开RGB文件,并事先建立好输出YUV文件的路径(利fopen函数)
第二步:计算RGB文件的长度(使用fseek和ftell函数),其实在本实验中,这一步也可以省略,因为我们已知是256256像素的RGB文件,也就是说RGB文件的长度为256256*3
第三步:设立缓冲区,利用fread函数读取RGB文件,并将RGB文件的中的数据全部写入缓冲区。
第四步:将RGB_BUFFER中混在一起的RGB数据分开到RED,GREEN和BLUE分别的BUFFER中,而后便可以通过分开后的BUFFER按照上面所述的方法计算相应的YUV值,并将之写入到YUV相应的BUFFER中
第五步:将YUV相应的BUFFER中的内容写出(利用fwrite函数),最后我们就可以在文件里看见一个生成的YUV文件。
第六步:释放缓冲区。
处理前的原图片:

具体代码实现:

#include<iostream>
#include"rgb2yuv.h"
using namespace std;

int main(int argc, char* argv[])
{
	FILE* RGB = NULL;
	FILE* YUV = NULL;
	int w = 256;
	int h = 256;

	if ((fopen_s(&RGB,"down.rgb", "rb")) != 0)
	{
		cout << "Failed to open the RGB file!" << endl;
	}
	else
	{
		cout << "Successfully opened down.rgb!" << endl;
	}
	if ((fopen_s(&YUV,"argv[2]", "wb")) != 0)
	{
		cout << "Failed to write in the YUV file!" << endl;
	}
	else
	{
		cout << "Successfully written YUV!" << endl;
	}
	
	fseek(RGB, 0L, SEEK_END);
	int size;
	size = ftell(RGB);
	fseek(RGB, 0L, SEEK_SET);//caculate the length of the rgb file

	unsigned char* RGB_BUFFER = new unsigned char[size];
	unsigned char* Y_BUFFER = new unsigned char[size / 3];
	unsigned char* U_BUFFER = new unsigned char[size / 12];
	unsigned char* V_BUFFER = new unsigned char[size / 12];

	RGB_BUFFER = new unsigned char[size];
	fread(RGB_BUFFER, sizeof(unsigned char), size, RGB);

	unsigned char* RED_BUFFER = new unsigned char[size / 3];
	unsigned char* GREEN_BUFFER = new unsigned char[size / 3];
	unsigned char* BLUE_BUFFER = new unsigned char[size / 3];
	//in order to caculate U,V,it will be more clearly to see if we figure out RGB
	for (int i = 0; i < size / 3; i++)
	{
		RED_BUFFER[i] = RGB_BUFFER[3 * i + 2];
	}
	for (int i = 0; i < size / 3; i++)
	{
		GREEN_BUFFER[i] = RGB_BUFFER[3 * i + 1];
	}
	for (int i = 0; i < size / 3; i++)
	{
		BLUE_BUFFER[i] = RGB_BUFFER[3 * i];
	}

	for (int i = 0; i < w * h; i++)//caculate Y
	{
		Y_BUFFER[i] = RED_BUFFER[i] * 0.2990 +
			          GREEN_BUFFER[i] * 0.5870 +
			          BLUE_BUFFER[i] * 0.1140;
		//Y=0.2990*Red+0.5870*Green+0.1440*Blue
		if (Y_BUFFER[i] > 235)
		{
			Y_BUFFER[i] = 235;
		}
		if (Y_BUFFER[i] < 16)
		{
			Y_BUFFER[i] = 16;
		}
		//In order to avoid the overload of the luminace, so we need to reserve some space
	}

	
	int u = 0;
	for (int i = 0; i < w * h;)//caculate u
	{
		U_BUFFER[u] = (RED_BUFFER[i] + RED_BUFFER[i + 1] + RED_BUFFER[i + w] + RED_BUFFER[i + w + 1]) / 4 * (-0.1684) +
			          (GREEN_BUFFER[i] + GREEN_BUFFER[i + 1] + GREEN_BUFFER[i + w] + GREEN_BUFFER[i + w + 1]) / 4 * (-0.3316) +
			          (BLUE_BUFFER[i] + BLUE_BUFFER[i + 1] + BLUE_BUFFER[i + w] + BLUE_BUFFER[i + w + 1]) / 4 * (0.500) + 128;
		if (U_BUFFER[u] > 235)
		{
			U_BUFFER[u] = 235;
		}
		if (U_BUFFER[u] < 16)
		{
			U_BUFFER[u] = 16;
		}
		if (i % 256 == 254)
		{
			i = i + 258;
		}
		else
		{
			i = i + 2;
		}
		u++;
	}

	int v = 0;
	for (int i = 0; i < w * h;)//caculate v
	{
		V_BUFFER[v] = (RED_BUFFER[i] + RED_BUFFER[i + 1] + RED_BUFFER[i + w] + RED_BUFFER[i + w + 1]) / 4 * (0.5) +
			          (GREEN_BUFFER[i] + GREEN_BUFFER[i + 1] + GREEN_BUFFER[i + w] + GREEN_BUFFER[i + w + 1]) / 4 * (-0.4187) +
			          (BLUE_BUFFER[i] + BLUE_BUFFER[i + 1] + BLUE_BUFFER[i + w] + BLUE_BUFFER[i + w + 1]) / 4 * (-0.0813) + 128;
		if (V_BUFFER[v] > 235)
		{
			V_BUFFER[v] = 235;
		}
		if (V_BUFFER[v] < 16)
		{
			V_BUFFER[v] = 16;
		}
		if (i % 256 == 254)
		{
			i = i + 258;
		}
		else
		{
			i = i + 2;
		}
		v++;
	}
	fwrite(Y_BUFFER, sizeof(unsigned char), w * h, YUV);
	fwrite(U_BUFFER, sizeof(unsigned char), w * h / 4, YUV);
	fwrite(V_BUFFER, sizeof(unsigned char), w * h / 4, YUV);

	free(RGB_BUFFER);
	free(Y_BUFFER);
	free(U_BUFFER);
	free(V_BUFFER);
	free(RED_BUFFER);
	free(GREEN_BUFFER);
	free(BLUE_BUFFER);
}

程序运行结果截图:

在这里插入图片描述
处理后用PYUV打开后存储的bmp图片:

第二部分的实验基本思路和实验一大同小异,需要注意的点仍然是YUV转化为RGB时的相关计算。
具体代码和处理后结果如下:

#include<iostream>
#include"yuv2rgb.h"
using namespace std;

int main(int argc, char* argv[])
{
	FILE* YUV = NULL;
	FILE* RGB = NULL;
	int w = 256;
	int h = 256;

	if ((fopen_s(&YUV, "argv[2].yuv", "rb")) != 0)
	{
		cout << "Failed to open the YUV file!" << endl;
	}
	else
	{
		cout << "File successfully opened!" << endl;
	}
	if ((fopen_s(&RGB, "argv[2]", "wb")) != 0)
	{
		cout << "Failed to write in the RGB file!" << endl;
	}
	else
	{
		cout << "Successfully written RGB!" << endl;
	}

	fseek(YUV, 0L, SEEK_END);
	int size;
	size = ftell(YUV);
	fseek(YUV, 0L, SEEK_SET);

	unsigned char* YUV_BUFFER = new unsigned char[size];
	unsigned char* Y_BUFFER = new unsigned char[size * 2 / 3];
	unsigned char* U_BUFFER = new unsigned char[size / 6];
	unsigned char* V_BUFFER = new unsigned char[size / 6];

	YUV_BUFFER = new unsigned char[size];
	fread(YUV_BUFFER, sizeof(unsigned char), size,YUV);

	unsigned char* RED_BUFFER = new unsigned char[size * 2 / 3];
	unsigned char* GREEN_BUFFER = new unsigned char[size * 2 / 3];
	unsigned char* BLUE_BUFFER = new unsigned char[size * 2 / 3];
	
	for (int i = 0; i < (size * 2 / 3); i++)
	{
		Y_BUFFER[i] = YUV_BUFFER[i];
	}
	for (int i = 0; i < (size * 1 / 6); i++)
	{
		U_BUFFER[i] = YUV_BUFFER[i + (size * 2 / 3)];
	}
	for (int i = 0; i < (size * 1 / 6); i++)
	{
		V_BUFFER[i] = YUV_BUFFER[i + (size * 5 / 6)];
	}
	int r = 0;
	for (int i = 0; i < size * 2 / 3;)
	{
		RED_BUFFER[i] = Y_BUFFER[i] + 1.4020 * (V_BUFFER[r] - 128);
		RED_BUFFER[i + 1] = Y_BUFFER[i+1] + 1.4020 * (V_BUFFER[r] - 128);
		RED_BUFFER[i+256] = Y_BUFFER[i+256] + 1.4020 * (V_BUFFER[r] - 128);
		RED_BUFFER[i+257] = Y_BUFFER[i+257] + 1.4020 * (V_BUFFER[r] - 128);
		if (i % 256 == 254)
		{
			i = i + 258;
		}
		else
		{
			i = i + 2;
		}
		r++;
	}

	int g = 0;
	for (int i = 0; i < size * 2 / 3;)
	{
		GREEN_BUFFER[i] = Y_BUFFER[i] - 0.3441 * (U_BUFFER[g] - 128) - 0.7139 * (V_BUFFER[g] - 128);
		GREEN_BUFFER[i+1] = Y_BUFFER[i+1] - 0.3441 * (U_BUFFER[g] - 128) - 0.7139 * (V_BUFFER[g] - 128);
		GREEN_BUFFER[i+256] = Y_BUFFER[i+256] - 0.3441 * (U_BUFFER[g] - 128) - 0.7139 * (V_BUFFER[g] - 128);
		GREEN_BUFFER[i+257] = Y_BUFFER[i+257] - 0.3441 * (U_BUFFER[g] - 128) - 0.7139 * (V_BUFFER[g] - 128);
		if (i % 256 == 254)
		{
			i = i + 258;
		}
		else
		{
			i = i + 2;
		}
		g++;
	}
	
	int b = 0;
	for (int i = 0; i < size * 2 / 3;)
	{
		BLUE_BUFFER[i] = Y_BUFFER[i] + 1.7718 * (U_BUFFER[b] - 128) - 0.0013 * (V_BUFFER[b] - 128);
		BLUE_BUFFER[i+1] = Y_BUFFER[i+1] + 1.7718 * (U_BUFFER[b] - 128) - 0.0013 * (V_BUFFER[b] - 128);
		BLUE_BUFFER[i+256] = Y_BUFFER[i+256] + 1.7718 * (U_BUFFER[b] - 128) - 0.0013 * (V_BUFFER[b] - 128);
		BLUE_BUFFER[i+257] = Y_BUFFER[i+257] + 1.7718 * (U_BUFFER[b] - 128) - 0.0013 * (V_BUFFER[b] - 128);
		if (i % 256 == 254)
		{
			i = i + 258;
		}
		else
		{
			i = i + 2;
		}
		b++;
	}
	

	fwrite(BLUE_BUFFER, sizeof(unsigned char), size * 2 / 3, RGB);
	fwrite(GREEN_BUFFER, sizeof(unsigned char), size * 2 / 3, RGB);
	fwrite(RED_BUFFER, sizeof(unsigned char), size * 2 / 3, RGB);

	free(YUV_BUFFER);
	free(Y_BUFFER);
	free(U_BUFFER);
	free(V_BUFFER);
	free(RED_BUFFER);
	free(GREEN_BUFFER);
	free(BLUE_BUFFER);

}

程序运行结果:
在这里插入图片描述
在这里插入图片描述

实验反思:
实验中的调试错误过程:
1.在昨晚rgb2yuv之后,做yuv2rgb的时候,复制之前生成的YUV文件到新的工程目录的时候,不要修改文件名,不要给YUV文件手动添加后缀.yuv,这样虽然添加之后YUV文件仍然可以正常打开,但是程序读取的时候会报错。
2.要清楚YUV文件存储时,其内部的存储顺序,以及在读取YUV和RGB文件的时候都要注意循环的写法。

实验结论:
转化后的YUV文件和RGB文件打开后的图片和原图片几乎没有明显差异。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值