第一次实验:彩色空间转换
实验目的
1.学会从计算和程序的角度分析问题
通过完成本实验,理解计算思维,即从问题出发,通过逐步分析和分解,把原问题转化
为可用程序方式解决的问题。在此过程中设计出一个解决方案。
2.进一步理解彩色空间的概念并掌握不同彩色空间转换的基本方程。
3.通过逐步设计程序,掌握编程细节:如查找表的设计,内存分配,对 U 和 V 信号进
行下采样,文件读写过程等。掌握程序调试的基本方法。
实验内容
掌握彩色空间转换的基本思想及转换公式
(1)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之间,需要进行归一化,对色差信号引入压缩
系数。归一化后的色差信号为:
U=-0.1684 * R-0.3316*G+0.5 * B
V=0.5 * R-0.4187 * G-0.0813 * B
YUV到RGB空间的转换
R = Y + 1.4075 *(V-128)
G = Y – 0.3455 * (U –128) – 0.7169 * (V –128)
B = Y + 1.779 * (U – 128)
(2) 码电平分配及数字表达式
- 亮电平信号量化后码电平分配
在对分量信号进行8比特均匀量化时,共分为256个等间隔的量化级。为了防止信号变动造成过载,在256级上端留20级,下端留16级作为信号超越动态范围的保护带。
- 色差信号量化后码电平分配
色差信号经过归一化处理后,动态范围为-0.5-0.5,让色差零电平对应码电平128,色差信号总共占225个量化级。在256级上端留15级,下端留16级作为信号超越动态范围
的保护带。
(3)色度格式
4:2:0格式是指色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半。
∴总结
实验第一部分
我们需要将给定RGB文件转换为YUV文件。
1.已知转换公式和电平分配,总结后得到具体计算方法如下:
Y=0.2990R+0.5870G+0.1140B
且Y>235时取235,<20时取20.
U=-0.1684 * R-0.3316*G+0.5 * B +128
V=0.5 * R-0.4187 * G-0.0813 * B+128
且U,V>240时取240,<20时取20.
2.计算U和V数值时需要注意,由于色差信号在水平和垂直方向的取样点数均为Y的一半,所以需要用上下左右的相邻四个像素点RGB值取平均值共同计算。
3.YUV值是按顺序存储。
YYYYYYYY
YYYYYYYY
UUUUVVVV
RGB是每个像素的RGB一起存储。
BGRBGRBGR……
因此在读写文件操作时要注意数组不要越界,读取数值要正确。
实验第二部分
我们需要将第一部分计算的YUV文件再转换回RGB文件。
1.已知转换公式和电平分配,总结后得到具体计算方法如下:
R = Y + 1.4075 *(V-128)
G = Y – 0.3455 * (U –128) – 0.7169 * (V –128)
B = Y + 1.779 * (U – 128)
2.计算时需要注意,由于色差信号在水平和垂直方向的取样点数均为Y的一半,所以一组U和V值对应的是上下左右相邻四个像素的RGB值,一起计算会简便一些。
代码如下。
第一部分:
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
static double RGB2YUV02990[256],
RGB2YUV05870[256],
RGB2YUV01140[256],
RGB2YUV01684[256],
RGB2YUV03316[256],
RGB2YUV04187[256],
RGB2YUV00813[256],
RGB2YUV05000[256];
void initLookUpTable()
{
for (int i = 0;i < 256;i++)
{
RGB2YUV02990[i] = (double)i * 0.2990;
RGB2YUV05870[i] = (double)i * 0.5870;
RGB2YUV01140[i] = (double)i * 0.1140;
RGB2YUV01684[i] = (double)i * 0.1684;
RGB2YUV03316[i] = (double)i * 0.3316;
RGB2YUV04187[i] = (double)i * 0.4187;
RGB2YUV00813[i] = (double)i * 0.0813;
RGB2YUV05000[i] = (double)i * 0.5;
}
}
int main(int argc, char* argv[])
{
FILE* rgbFile=NULL;
FILE* yuvFile=NULL;
//输入输出文件
if(fopen_s(&rgbFile,"E:\\数据压缩\\20200322\\down.rgb", "rb")!=0)
{
cout << "Failed to open the RGB file!" << endl;
}
else
{
cout << "Successfully opened down.rgb!" << endl;
}
if ((fopen_s(&yuvFile, "E:\\数据压缩\\20200322\\down.yuv", "wb")) != 0)
{
cout << "Failed to write in the YUV file!" << endl;
}
else
{
cout << "Successfully written YUV!" << endl;
}
int width = 256;
int height = 256;
int size = 256 * 256;
unsigned char* rgb_b = NULL;
unsigned char* r_b = NULL;
unsigned char* g_b = NULL;
unsigned char* b_b = NULL;
unsigned char* y_b = NULL;
unsigned char* u_b = NULL;
unsigned char* v_b = NULL;
//创建动态数组
rgb_b = (unsigned char*)malloc(sizeof(char) * size * 3);
r_b = (unsigned char*)malloc(sizeof(char) * size);
g_b = (unsigned char*)malloc(sizeof(char) * size);
b_b = (unsigned char*)malloc(sizeof(char) * size);
y_b = (unsigned char*)malloc(sizeof(char) * size);
u_b = (unsigned char*)malloc(sizeof(char) * size / 4);
v_b = (unsigned char*)malloc(sizeof(char) * size / 4);
//读入数据
fread(rgb_b, sizeof(unsigned char), size * 3, rgbFile);
//把rgb文件的数据分别读到r g b三个数组中
for (int i = 0; i < size; i++)
{
b_b[i] = rgb_b[3 * i];
g_b[i] = rgb_b[3 * i + 1];
r_b[i] = rgb_b[3 * i + 2];
}
//提前计算数据方便算yuv三个分量
initLookUpTable();
//计算y分量
for (int i = 0; i < size; i++)
{
y_b[i] = RGB2YUV02990[r_b[i]] + RGB2YUV05870[g_b[i]] + RGB2YUV01140[b_b[i]];
if (y_b[i] < 16)
{
y_b[i] = 16;
}
else if (y_b[i] > 235)
{
y_b[i] = 235;
}
}
//计算uv分量
int u = 0;
int i = 0;
while(i < size)
{
u_b[u] = 128 -(RGB2YUV01684[r_b[i]] + RGB2YUV01684[r_b[i + 1]] + RGB2YUV03316[r_b[i + width]] + RGB2YUV03316[r_b[i + width + 1]]) / 4 +
-(RGB2YUV03316[g_b[i]] + RGB2YUV03316[g_b[i + 1]] + RGB2YUV03316[g_b[i + width]] + RGB2YUV03316[g_b[i + width + 1]]) / 4 +
(RGB2YUV05000[b_b[i]] + RGB2YUV05000[b_b[i + 1]] + RGB2YUV05000[b_b[i + width]] + RGB2YUV05000[b_b[i + width + 1]]) / 4;
v_b[u] = 128 -(RGB2YUV00813[b_b[i]] + RGB2YUV00813[b_b[i + 1]] + RGB2YUV00813[b_b[i + width]] + RGB2YUV00813[b_b[i + width + 1]]) / 4 +
-(RGB2YUV04187[g_b[i]] + RGB2YUV04187[g_b[i + 1]] + RGB2YUV04187[g_b[i + width]] + RGB2YUV04187[g_b[i + width + 1]]) / 4 +
(RGB2YUV05000[r_b[i]] + RGB2YUV05000[r_b[i + 1]] + RGB2YUV05000[r_b[i + width]] + RGB2YUV05000[r_b[i + width + 1]]) / 4;
if (u_b[u] < 16)
{
u_b[u] = 16;
}
else if (u_b[u] > 240)
{
u_b[u] = 240;
}
if (v_b[u] < 16)
{
v_b[u] = 16;
}
else if (v_b[u] > 239)
{
v_b[u] = 239;
}
if (i % width == 254)
{
i = i + 258;
}
else
{
i = i + 2;
}
u++;
}
//写入yuv文件
fwrite(y_b, sizeof(unsigned char), size, yuvFile);
fwrite(u_b, sizeof(unsigned char), size/4, yuvFile);
fwrite(v_b, sizeof(unsigned char), size/4, yuvFile);
//释放buffer
free(rgb_b);
free(r_b);
free(g_b);
free(b_b);
free(y_b);
free(u_b);
free(v_b);
fclose(rgbFile);
fclose(yuvFile);
return 0;
}
第二部分
#include<iostream>
#include<fstream>
#include<math.h>
#include<string>
using namespace std;
static double YUV2RGB14075V[256],
YUV2RGB03455U[256],
YUV2RGB07169V[256],
YUV2RGB01779U[256];
/*void initLookUpTable()
{
for (int i = 0;i < 256;i++)
{
YUV2RGB14075V[i] = (double)(i-128) * 1.4075;
YUV2RGB03455U[i] = (double)(i - 128) * (-0.3455);
YUV2RGB07169V[i] = (double)(i-128) * (-0.7169);
YUV2RGB01779U[i] = (double)i * 1.1779;
}
}*/
int main(void)
{
FILE* yuvFile = NULL;
FILE* rgbFile = NULL;
//输入输出文件
if (fopen_s(&yuvFile, "E:\\数据压缩\\20200322\\down.yuv", "rb") != 0)
{
cout << "Failed to open the YUV file!" << endl;
}
else
{
cout << "Successfully opened down.yuv!" << endl;
}
if ((fopen_s(&rgbFile, "E:\\数据压缩\\20200322\\downed.rgb", "wb")) != 0)
{
cout << "Failed to write in the RGB file!" << endl;
}
else
{
cout << "Successfully written downed.rgb!" << endl;
}
int width = 256;
int height = 256;
int size = 256 * 256;
unsigned char* yuv_b = NULL;
unsigned char* rgb_b = NULL;
unsigned char* r_b = NULL;
unsigned char* g_b = NULL;
unsigned char* b_b = NULL;
unsigned char* y_b = NULL;
unsigned char* u_b = NULL;
unsigned char* v_b = NULL;
yuv_b = (unsigned char*)malloc(sizeof(char) * size * 1.5);
rgb_b = (unsigned char*)malloc(sizeof(char) * size * 3);
y_b = (unsigned char*)malloc(sizeof(char) * size);
u_b = (unsigned char*)malloc(sizeof(char) * size / 4);
v_b = (unsigned char*)malloc(sizeof(char) * size / 4);
r_b = (unsigned char*)malloc(sizeof(char) * size);
g_b = (unsigned char*)malloc(sizeof(char) * size);
b_b = (unsigned char*)malloc(sizeof(char) * size);
fread(yuv_b, sizeof(unsigned char), size * 1.5, yuvFile);
/*for (int i = 0; i < size*1.5;i++)
{
cout << yuv_b[i] << ' ';
if (i % 256 == 0) cout << endl;
}*/
int count = 0;
for(int i = 0; i < size; i++)
{
y_b[i] = yuv_b[i];
count++;
}
for (int i = 0; i < size / 4; i++)
{
u_b[i] = yuv_b[ count + i];
count++;
}
for (int i = 0; i < size / 4; i++)
{
v_b[i] = yuv_b[ count + i];
count++;
}
//initLookUpTable();
//R = Y + 1.4075 *(V-128)
//G = Y – 0.3455 * (U –128) – 0.7169 * (V –128)
//B = Y + 1.779 * (U – 128)
int rcount = 0;
int i = 0;
while(i<size)
{
r_b[i] = y_b[i] + 1.4075 * (v_b[rcount] - 128); //YUV2RGB14075V[v_b[rcount]];
r_b[i + 1] = y_b[i + 1] + 1.4075 * (v_b[rcount] - 128); //YUV2RGB14075V[v_b[rcount]];
r_b[i + width] = y_b[i + width] + 1.4075 * (v_b[rcount] - 128); //YUV2RGB14075V[v_b[rcount]];
r_b[i + width + 1] = y_b[i + width + 1] + 1.4075 * (v_b[rcount] - 128); //YUV2RGB14075V[v_b[rcount]];
if (i % width == 254)
{
i += 256;
}
else
{
i += 2;
}
rcount++;
}
int gcount = 0;
i = 0;
while(i<size/4)
{
g_b[i] = y_b[i] -0.3455 * (u_b[gcount] - 128) - 0.7169 * (v_b[gcount] - 128);//YUV2RGB03455U[u_b[gcount]] + YUV2RGB07169V[v_b[gcount]];
g_b[i + 1] = y_b[i + 1] - 0.3455 * (u_b[gcount] - 128) - 0.7169 * (v_b[gcount] - 128);//YUV2RGB03455U[u_b[gcount]] + YUV2RGB07169V[v_b[gcount]];
g_b[i + width] = y_b[i + width] + -0.3455 * (u_b[gcount] - 128) - 0.7169 * (v_b[gcount] - 128);//YUV2RGB03455U[u_b[gcount]] + YUV2RGB07169V[v_b[gcount]];
g_b[i + width + 1] = y_b[i + width + 1] + -0.3455 * (u_b[gcount] - 128) - 0.7169 * (v_b[gcount] - 128);//YUV2RGB03455U[u_b[gcount]] + YUV2RGB07169V[v_b[gcount]];
if (i % width == 254)
{
i += 256;
}
else
{
i += 2;
}
gcount++;
}
int bcount = 0;
i = 0;
while(i<size/4)
{
b_b[i] = y_b[i] + 1.779 * (u_b[bcount]-128);//YUV2RGB01779U[u_b[bcount]];
b_b[i + 1] = y_b[i + 1] + 1.779 * (u_b[bcount]-128);//+ YUV2RGB01779U[u_b[bcount]];
b_b[i + width] = y_b[i + width] + 1.779 * (u_b[bcount]-128);//+ YUV2RGB01779U[u_b[bcount]];
b_b[i + width + 1] = y_b[i + width + 1] + 1.779 * (u_b[bcount]-128);//+ YUV2RGB01779U[u_b[bcount]];
if (i % width == 254)
{
i += 256;
}
else
{
i += 2;
}
bcount++;
}
for (int i = 0; i < size; i++)
{
rgb_b[i * 3] = b_b[i];
rgb_b[i * 3 + 1] = g_b[i];
rgb_b[i * 3 + 2] = r_b[i];
}
fwrite(rgb_b, sizeof(unsigned char), size * 3, rgbFile);
free(yuv_b);
free(rgb_b);
free(y_b);
free(u_b);
free(v_b);
free(r_b);
free(g_b);
free(b_b);
fclose(yuvFile);
fclose(rgbFile);
return 0;
}
错误反思
在调试程序时出现了多次堆栈溢出的报错,在程序多个地方加入输出语句以后及时改正了。以后再处理动态数组的问题时要多加注意