1、 RGB颜色空间
RGB颜色空间 RGB(Red,Green,Blue)是显示和保存彩色图像的最常见的色彩空间,它通过CIE规定的三基色,即红、绿、蓝三种基色的加权混合得到各种颜色。这里可用图1的单位立方体和图2的加法混色原理图来说明。三维空间的3个轴分别与R(红)、G(绿)和 B(蓝)3个分量相对应。原点对应黑色,与原点最远的顶点对应白色。在这个模型中,从黑到白的灰度值分布在从黑色原点到白色顶点的连线上,每个分量的取值范围都是0到255。[1]
RGB颜色空间的加法混色原理使得 RGB 彩色立方体中对应的其它颜色都有一个唯一的空间位置,这些颜色可以通过 R,G,B三个颜色分量通过加法合成得到。加权公式如下:
C=α*R+β*G+γ*B
公式中的 C 表示彩色立方体对应的各种颜色值,当 R=G=B 时,对应不同的 R,G,B值是C表示灰度值变化。且α,β,γ 满足以下关系:
α +β +γ =1
图1 RGB彩色立方体 图2 加法混色原理图
2、YUV颜色空间
YUV 颜色空间常用于彩色电视系统中,其原理是得到的彩色图像的信号经过分色处理,然后分别放大校正转化为 RGB 颜色空间中的R,G,B上的分量,经过转换,最后获得亮度信号 Y 和两个色差信号 R-Y,B-Y 即表示为U和V。YUV 颜色空间中亮度信号 Y 和色度信号 U,V 是分离的,如果输出信号中只有Y 信号分量,则可表示彩色图像的灰度图像。彩色电视系统中采用 YUV 颜色空间也是为了用亮度信号 Y 解决与黑白电视兼容的问题,这样调整之后,黑白电视就可以接受彩色信号。[2]
YCbCr颜色空间是由YUV颜色空间派生的一种颜色空间,主要用于数字电视系统中。从RGB到YCbCr的转换中,输入、输出都是8位二进制格式。它其实是YUV经过缩放和偏移的翻版。其中Y与YUV中的Y含义一致,Cb,Cr同样都指色彩,只是在表示方法上不同而已。
RGB色彩模型和YUV色彩模型的转换关系是:
或:
3、视频压缩
视频压缩技术主要基于视频数据和人类视觉系统的研究分析,通过去除原始视频信号中的冗余信息来实现压缩。视频压缩主要基于下面两个事实:
首先,视频数据中有许多的冗余数据,冗余的数据主要分为三类:空间冗余、时间冗余和统计冗余。空间冗余指同一帧图像内部数据有较强相关性;时间冗余指连续图像之间数据存在较强的相关性;统计冗余指数据表达方法上的冗余。
其次,视频信号的接收者是人,人的视觉系统也是一个信号处理系统,它对图像各个组成部分的敏感程度不同,特别是对高频信息和色差信号不敏感。图像信息中不同部分对主观质量的影响不同,所以可以把视频信息中人眼不敏感的部分弱化处理,而尽量保存人眼较为敏感的部分。
基于这两点,视频压缩技术就是保留视频信号中人眼比较敏感的信息,对不敏感信号和冗余信息进行压缩,以适用于存储或传输的编码技术。[3]
4、YUV部分取样
广播、视频和成像标准使用YUV色彩空间还有一个重要的原因,就是与人的视觉系统有关。人类的眼睛对低频信号比对高频信号具有更高的敏感度。事实上,人类的眼睛对明视度的改变也比对色彩的改变要敏感的多。因此对人类而言, Y分量比U分量重要,根据人眼的这特征,在不使用任何复杂算法的前提下,可以适当地抛弃U和V分量以达到压缩的目的,这就是部分取样。
部分取样的常见方式有YUV444(无压缩)、YUV422(33.3%压缩)、YUV411(50.0%压缩)、YUV420(50.0%压缩)等,其中的数字表明了Y,U,V 3个分量的取样比例,即各分量水平取样因子与垂直取样因子乘积的比例。
以N× N大小的方阵为例,假设:对Y取N×N个数据单元,即水平取样因子Hy= N;垂直取因子Vy= N。对U和V均取M1× M2个数据单元(0≤ M1,M2≤ N),即:水平取样因子Hu= M1, Hv= M1;垂直取样因子Vu= M2, Vv= M2。则Y、U、V取样比为(N× N)∶(M1×M2)∶(M1× 2)。
若取N= 2, M1= 2, M2= 2,这就是YUV444的一种取样方式,没有压缩;
若取N= 2, M1= 2, M2= 1,这就是YUV422的一种取样方式,压缩比33.3%;
若取N= 2, M1= 1, M2= 1,这就是YUV420的一种取样方式,压缩比50.0%。
注意,4∶2∶0并非是色差信采样为0,而是和4∶1∶1相比,在水平方向上提高1倍色差采样频率,在垂直方向上以CrCb间的方式减小一半色差采样。[4]
如果用三个图来直观地表示采样的方式,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量,则图示如下:
图3 采样方式图
YUV 4:4:4采样,每一个Y对应一组UV分量;YUV 4:2:2采样,每两个Y共用一组UV分量;YUV 4:2:0采样,每四个Y共用一组UV分量。[5]
5、YUV存储方式
(1)YUV4:4:4
YUV三个信道的抽样率相同,因此在生成的图像里,每个象素的三个分量信息完整(每个分量通常 8 比特),经过 8 比特量化之后,未经压缩的每个像素占用 3 个字节。
下面的四个像素为 : [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放的码流为 : Y0 U0 V0 Y1 U1 V1 Y2 U2V2 Y3 U3 V3
(2)YUV4:2:2
每个色差信道的抽样率是亮度信道的一半,所以水平方向的色度抽样率只是 4:4:4 的一半。对非压缩的 8 比特量化的图像来说,每个由两个水平方向相邻的像素组成的宏像 素需要占用 4 字节内存。
下面的四个像素为: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放的码流为: Y0 U0 Y1 V1 Y2 U2 Y3 V3
映射出像素点为: [Y0 U0 V1] [Y1 U0 V1] [Y2 U2V3] [Y3 U2 V3]
(3)YUV4:1:1
4:1:1的色度抽样,是在水平方向上对色度进行 4:1 抽样。对于低端用户和消费类产品这仍然是可以接受的。对非压缩的 8 比特量化的视频来说,每个由 4 个水平方向相邻 的像素组成的宏像素需要占用 6 字节内存。
下面的四个像素为 : [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
存放的码流为 : Y0 U0 Y1 Y2 V2 Y3
映射出像素点为: [Y0 U0 V2] [Y1 U0 V2] [Y2 U0V2] [Y3 U0 V2]
(4)YUV4:2:0
4:2:0并不意味着只有Y,Cb 而没有Cr分量。它指得是对每行扫描线来说,只有一种色度分量以 2:1 的抽样率存储。相邻的扫描行存储不同的色度分量,也就是说,如果一 行是 4:2:0的话,下一行就是 4:0:2,再下一行是4:2:0... 以此类推。对每个色度分量来说,水平方向和竖直方向的抽样率都是 2:1,所以可以说色度的抽样率是 4:1。对非压 缩的8比特量化的视频来说,每个由2x2个2行2列相邻的像素组成的宏像素需要占用6字节内存。[6]
6、音频压缩和保存
WAV为微软公司(Microsoft)开发的一种声音文件格式,被Windows平台及其应用程序所广泛支持,该格式也支持MSADPCM,MPEG,CCITT A LAW等多种压缩运算法,支持多种音频数字,取样频率和声道。[7]
音频数据都是双极性信号,它的采样率跟人耳能听到的频率范围有关。
常见的声音文件主要有两种,分别对应于单声道(11.025KHz采样率、8Bit的采样值)和双声道(44.1KHz采样率、16Bit的采样值),标准格式化的WAV文件采样频率为44100Hz,采样比特为16bit。则声音的强弱范围可划分成:20lg216=96(dB)个等级,因而动态范围较大。
在Windows环境下,大部分的多媒体文件都依循着一种结构来存放信息,这种结构称为"资源互换文件格式"(Resources lnterchangeFile Format),简称RIFF。声音的WAV文件也是由此结构衍生出来的。RIFF可以看做是一种树状结构,其基本构成单位为chunk,犹如树状结构中的节点,每个chunk由RIFFWAVE Chunk, Format Chunk, Fact Chunk(可选),Data Chunk所组成。
图4 块的结构示意图
Data Chunk是真正保存wav数据的地方,以'data'作为该Chunk的标示。然后是数据的大小。紧接着就是wav数据。
表1 Data Chunk
所占字节数
具体内容
ID
4 Bytes
'data'
Size
4 Bytes
data
根据Format Chunk中的声道数以及采样bit数,wav数据的bit位置可以分成以下几种形式:
表2 wav数据bit位置安排方式
单声道
8bit量化
取样1
取样2
取样3
取样4
声道0
声道0
声道0
声道0
双声道
8bit量化
取样1
取样2
声道0(左)
声道1(右)
声道0(左)
声道1(右)
单声道
16 bit量化
取样1
取样2
声道0
(低位字节)
声道0
(高位字节)
声道0
(低位字节)
声道0
(高位字节)
双声道
取样1
16 bit量化
声道0(左)
(低位字节)
声道0(左)
(高位字节)
声道1(右)
(低位字节)
声道1(右)
(高位字节)
附:YUV Player:http://blog.csdn.net/leixiaohua1020/article/details/50466201
BMP转换成YUV代码:
main.cpp
#include
#include
#include
#include "bmp2yuv.h"
int main(int argc, char** argv)
{
FILE *bmpFile=NULL,*yuvFile=NULL;
BITMAPFILEHEADER File_header;
BITMAPINFOHEADER Info_header;
unsigned char *rgbData=NULL;
unsigned char *yBuff=NULL;
unsigned char *uBuff=NULL;
unsigned char *vBuff=NULL;
if((bmpFile=fopen(argv[1],"rb"))==NULL)
{
printf("bmp file open failed!\n");
exit(0);
}
if((yuvFile=fopen(argv[2],"wb"))==NULL)
{
printf("yuv file failed!");
exit(0);
}
if(fread(&File_header,sizeof(BITMAPFILEHEADER),1,bmpFile)!=1)
{
printf("read file header error!");
exit(0);
}
if(File_header.bfType!=0x4d42)
{
printf("Not a bmp file!");
exit(0);
}
if(fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmpFile)!=1)
{
printf("read info header error!");
exit(0);
}
unsigned long width,height;
if((Info_header.biWidth%4)==0)
width=Info_header.biWidth;
else
width=(Info_header.biWidth*Info_header.biBitCount+31)/32*4;
if((Info_header.biHeight%2)==0)
height=Info_header.biHeight;
else
height=Info_header.biHeight+1;
rgbData=(unsigned char*)malloc(width*height*3);
memset(rgbData,0,height*width*3);
yBuff=(unsigned char*)malloc(width*height);
uBuff=(unsigned char*)malloc((width*height)/4);
vBuff=(unsigned char*)malloc((width*height)/4);
printf("this is a %d bits imager!\n",Info_header.biBitCount);
printf("\nbmp size:\t%dX%d\n",Info_header.biWidth,Info_header.biHeight);
ReadRGB (bmpFile,File_header,Info_header,rgbData);
RGB2YUV (width,height,rgbData,yBuff,uBuff,vBuff,0);
if(WriteYUV(yBuff,uBuff,vBuff,width*height,yuvFile))
printf("write yuv file successful!\n");
else
printf("write yuv file failed!\n");
if(bmpFile) fclose(bmpFile);
if(yuvFile) fclose(yuvFile);
free(rgbData);
free(yBuff);
free(uBuff);
free(vBuff);
return(0);
}
bmp2yuv.cpp
#include
#include
#include
#include
#include "bmp2yuv.h"
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
void InitLookupTable();
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip)
{
static int init_done = 0;
long i, j, size;
unsigned char *r, *g, *b;
unsigned char *y, *u, *v;
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
unsigned char *y_buffer, *u_buffer, *v_buffer;
unsigned char *sub_u_buf, *sub_v_buf;
if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}
// check to see if x_dim and y_dim are divisible by 2
if ((x_dim % 2) || (y_dim % 2)) return 1;
size = x_dim * y_dim;
// allocate memory
y_buffer = (unsigned char *)y_out;
sub_u_buf = (unsigned char *)u_out;
sub_v_buf = (unsigned char *)v_out;
u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
if (!(u_buffer && v_buffer))
{
if (u_buffer) free(u_buffer);
if (v_buffer) free(v_buffer);
return 2;
}
b = (unsigned char *)bmp;
y = y_buffer;
u = u_buffer;
v = v_buffer;
// convert RGB to YUV
if (!flip) {
for (j = 0; j < y_dim; j ++)
{
y = y_buffer + (y_dim - j - 1) * x_dim;
u = u_buffer + (y_dim - j - 1) * x_dim;
v = v_buffer + (y_dim - j - 1) * x_dim;
for (i = 0; i < x_dim; i ++) {
g = b + 1;
r = b + 2;
*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);
*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 3;
y ++;
u ++;
v ++;
}
}
} else
{
for(j=0;j
References:
[1]. 向方明等, YUV到RGB颜色空间转换算法研究. 现代电子技术, 2012(22): 第65-68页.
[2]. 基于YUV颜色空间的边缘检测算法研究, 2013, 杭州电子科技大学. 第 62页.
[3]. 方健, 新一代视频压缩标准算法和应用研究, 2008, 浙江大学. 第 125页.
[4]. 邵丹.YUV与RGB之间的转换. 长春大学学报, 2004. 14(04): 第51-53页.
[5]. 温宗亮.YUV采样格式与存储格式:http://wenzongliang.iteye.com/blog/1902608.
[6]. RGB和YUV之比较: http://blog.csdn.net/qfnu08zzr/article/details/6763159
[7]. WAV格式常见的几种压缩形式:http://blog.csdn.net/stelalala/article/details/17058755.