实验原理
一、YUV与RGB空间的转换
在电视原理中我们学到过YUV与RGB的转换公式如下:
Y=0.2990R+0.5870G+0.1140B
V=0.7010R-0.5870G-0.1140B
U=-0.2990R-0.5870G+0.8860B
除此之外,还需要对两个色差信号进行归一化,使得压缩后的色差信号动态范围控制在0.5以内,所以最终的转换公式为:
Y=0.2990R+0.5870G+0.1140B
U=-0.1684R-0.3316G+0.5B
V=0.5R-0.4187G-0.0813B
二、量化后的码电平分配
对YUV信号进行8bit量化,共256个量化等级。Y为亮度信号,其上端留下20级防止信号造成过载,下端留下16级作为动态保护带;UV信号则是上端留下15级,下端留下16级,除此之外,UV信号还需进行零电平调整,使其零电平对应码电平+128。
三、4:2:0色度亚采样
4:2:0格式下,色差信号的取样频率为亮度信号的四分之一。
编程思路
1、根据RGB文件的格式,将其数据存入开辟的缓存空间中,再分离R、G、B三个分量,在文件中数据顺序为BGRBGRBGR…。
2、开辟YUV的缓存空间,根据公式和读取的RGB分量计算YUV分量,注意量化码电平分配和UV信号加128。
3、开辟新的UV空间,进行4:2:0格式采样,计算新的UV时取宽高的4个数据为一组求平均值。
3、将YUV分量写入新建的yuv文件,在YUV文件中数据顺序为:所有Y所有U所有V。
C++具体代码:
RGB2YUV:
#include<stdio.h>
#include<stdlib.h>
#pragma warning(disable:4996);
int main(int argc, char *argv[])
{
FILE*yuvfile = NULL;
FILE*rgbfile = NULL;
unsigned char *r, *g, *b; int i, j;
const int width = 256; const int height = 256;
if(!(rgbfile = fopen(argv[1], "rb")))
{
printf("Fileopen error!");
}
else
{
printf("Fileopen success!");
}
if(!(yuvfile = fopen(argv[2], "wb")))
{
printf("Fileopen error!");
}
else
{
printf("Fileopen success!");
}//打开文件
unsigned char *rgbBUF = (unsigned char*)malloc(sizeof(unsigned char) * 3 * width *height);
unsigned char *yBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height);
unsigned char *uBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height);
unsigned char *vBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height);//缓冲区
fread(rgbBUF,sizeof(unsigned char), width* height*3,rgbfile);
for(int i = 0; i < width*height; i++)
{
yBUF[i]= 0.299 * rgbBUF[3 * i + 2] + 0.587 * rgbBUF[3 * i + 1] + 0.114 * rgbBUF[3 *i];
uBUF[i]= -0.1684* rgbBUF[3 * i + 2] - 0.3316 * rgbBUF[3 * i + 1]+ 0.5 * rgbBUF [3 * i]+ 128;
vBUF[i]= 0.5 * rgbBUF[3 * i + 2] - 0.4187 * rgbBUF[3 * i + 1] - 0.0183 * rgbBUF[3 * i]+ 128;
if(yBUF[i] > 235)
yBUF[i]= 235;
if(yBUF[i] < 16)
yBUF[i]= 16;
if(uBUF[i] > 240)
uBUF[i]= 240;
if(uBUF[i] < 16)
uBUF[i]= 16;
if(vBUF[i] > 240)
vBUF[i]= 240;
if(vBUF[i] < 16)
vBUF[i]= 16;
}//计算RGB转YUV
unsigned char *u = (unsigned char*)malloc(sizeof(unsigned char) * width * height*0.25);
unsigned char *v = (unsigned char*)malloc(sizeof(unsigned char) * width *height*0.25);//用于4:2:0采样写入的UV分量
for(i = 0; i <height; i += 2)
{
for(j = 0; j < width; j += 2)
{
u[i/ 2*width/2+j/2] = (uBUF[i*width+j] + uBUF[i*width+ 1+j] + uBUF[(i+1)*width +j] + uBUF[(i+1)*width + j+1]) / 4;
v[i/ 2 * width / 2 + j / 2] = (vBUF[i*width + j] + vBUF[i*width + 1 + j] + vBUF[(i+ 1)*width + j] + vBUF[(i + 1)*width + j + 1]) / 4;
}
}//uv分量下采样*/
fwrite(yBUF,sizeof(unsigned char), width*height, yuvfile);
fwrite(u,sizeof(unsigned char), width*height / 4, yuvfile);
fwrite(v,sizeof(unsigned char), width*height / 4, yuvfile);//转换后数据写入yuv文件
free(rgbBUF);
free(yBUF);
free(uBUF);
free(vBUF);
free(u);
free(v);
fclose(rgbfile);
fclose(yuvfile);
return 0;
}
运行程序后用YUVviewer查看得到的yuv文件:
YUV2RGB
YUV2RGB的代码可以在RGB2YUV的代码上修改,其思路基本相同,即读取YUV文件,开辟缓存分配数据,计算转换,再写入RGB文件中,其中要注意的有:
1、根据计算系数逆矩阵可以得到YUV2RGB的转换公式:
R=1.000Y+1.4020(V−128)
G=1.000Y−0.3441(U−128)−0.7139(V−128)
B=1.000Y+1.7718(U−128)−0.0013(V−128)
2、UV分量减去128。
3、开辟新的UV空间,大小为原来的4倍,将读取的UV数据写入。
具体代码:
#include<stdio.h>
#include<stdlib.h>
#pragma warning(disable:4996);
int main(int argc, char *argv[])
{
FILE *yuvfile = NULL;
FILE *rgbfile = NULL;
unsigned char *r, *g, *b; int i, j;
const int width = 256; const int height = 256;
if(!(yuvfile = fopen(argv[1], "rb")))
{
printf("Fileopen error!");
}
else
{
printf("Fileopen success!");
}
if(!(rgbfile = fopen(argv[2], "wb")))
{
printf("Fileopen error!");
}
else
{
printf("Fileopen success!");
}//打开文件
unsigned char *rgbBUF = (unsigned char*)malloc(sizeof(unsigned char) * 3 * width *height);
unsigned char *yuvBUF = (unsigned char*)malloc(sizeof(unsigned char) *width * height*1.5);
unsigned char *yBUF = (unsigned char*)malloc(sizeof(unsigned char) * width*height);
unsigned char *uBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height/4);
unsigned char *vBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height/4);
fread(yuvBUF,sizeof(unsigned char), width * height*1.5 , yuvfile);
for(i = 0; i < width*height; i++)
{
yBUF[i]= yuvBUF[i];
}
for(i = width*height; i < width*height*1.25; i++)
{
uBUF[i-width*height]= yuvBUF[i];
}
for(i = width * height*1.25; i < width*height*1.5; i++)
{
vBUF[i- width * height - width * height / 4] = yuvBUF[i];
}
unsigned char *u = (unsigned char*)malloc(sizeof(unsigned char) * width * height);
unsigned char *v = (unsigned char*)malloc(sizeof(unsigned char) * width * height);
for(i = 0; i < height; i += 2)
{
for(j = 0; j < width; j += 2)
{
u[i*width+ j] = uBUF[i / 2*width / 2 + j / 2];
u[i*width+ j+1] = uBUF[i / 2* width / 2 + j / 2];
u[(i+1)*width+ j] = uBUF[i / 2 * width / 2 + j / 2];
u[(i+1)*width+ j+1] = uBUF[i / 2 * width / 2 + j / 2];
v[i*width+ j] = vBUF[i / 2 * width / 2 + j / 2];
v[i*width+ j + 1] = vBUF[i / 2 * width / 2 + j / 2];
v[(i+ 1)*width + j] = vBUF[i / 2 * width / 2 + j / 2];
v[(i+ 1)*width + j + 1] = vBUF[i / 2 * width / 2 + j / 2];
}
}
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
rgbBUF[3* (i*width + j)] = yBUF[i*width + j] + 1.7718 * (u[i*width + j] - 128) - 0.0013* (v[i*width + j] - 128);
rgbBUF[3* (i*width + j) + 1] = yBUF[i*width + j] - 0.3441 * (u[i*width + j] - 128) -0.7139 * (v[i*width + j] - 128);
rgbBUF[3* (i*width + j) + 2] = yBUF[i*width + j] + 1.4020 *(v[i*width + j] - 128);
}
}
fwrite(rgbBUF,sizeof(unsigned char), width*height*3, rgbfile);
free(rgbBUF);
free(yBUF);
free(uBUF);
free(vBUF);
free(u);
free(v);
fclose(rgbfile);
fclose(yuvfile);
return 0;
}
运行程序得到RGB文件,用YUVviewer查看: