一、实验原理
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;
}