YUV和RGB是什么?干嘛用的?我们如果做音视频相关的工作这些基础概念是一定要理解的,而且展开来说很杂,怎么能够用一句话来概括一个个概念,以及作用是很重要的。
像素点上的颜色要被量化
,于是便有了YUV和RGB两种量化标准。YUV模型是根据一个亮度(Y分量)和两个色度(UV分量)来定义颜色空间。只采用Y分量来展示图像就构成了我们以前看的黑白电影,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题
。并且,YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用很少的频宽。
Y′UV,YUV,YCbCr,YPbPr都是指的YUV颜色空间
,从历史的演变来说,其中YUV和Y’UV通常用来编码电视的模拟信号,而YCbCr则是用来描述数字的视频信号,适合视频与图片压缩以及传输,例如MPEG、JPEG。但在现今,YUV通常已经在电脑系统上广泛使用。
紧缩格式(packedformats)
:将Y、U、V值存储成MacroPixels数组,和RGB的存放方式类似。
平面格式(planarformats)
:将Y、U、V的三个分量分别存放在不同的矩阵中,Y , U , V分别在不同平面,也就是有三个平面。
下面解释一下两种格式以及适合使用的场景。
紧缩格式(packedformat)
中的YUV是混合在一起的,对于YUV4:4:4格式
而言,用紧缩格式很合适的,因此就有了UYVY、YUYV等。
平面格式(planarformats)
是指每Y分量,U分量和V分量都是以独立的平面组织的,也就是说所有的U分量必须在Y分量后面,而V分量在所有的U分量后面,此一格式适用于采样(subsample)
。
我们可以在清晰度不受到大的损失的前提下降低数据量,于是就有了色度抽样
。下面是具体解释:
由于人眼对色度的敏感度不及对亮度的敏感度,图像的色度分量不需要有和亮度分量相同的清晰度,所以许多视频系统在色差通道上进行较低(相对亮度通道)清晰度(例如,抽样频率)的抽样。这样在不明显降低画面质量的同时降低了视频信号的总带宽。因抽样而丢失的色度值用内插值,或者前一色度值来替代。
YUV中三个分量都完整采样的情况下三个分量通常各8bit,也就是一个像素有8*3bit也就是占用3Byte。为了节省带宽,也就是数据量我们一般对UV分量进行非完全采样,这就有了色度抽样
。
色度抽样的概念
:在数位图像处理领域中,色度抽样是指在表示图像时使用较亮度信息为低的分辨率来表示色彩(色度)信息。
从码流中取出一组组YUV数据,组装成能被显示设备识别的数据。
这是无损失UV分量
的一种抽样方式,三个分量都占用8bit,如下:
码流:Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3
将被映射为下面的四个像素:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
这是最好的色度抽样比率(实际上它完整的表示了原先的色度信息)
,作为一种中间格式,它被用在高端的底片扫描仪和影片后期处理上。需要注意的是,有时候4:4:4(就像在HDCAM SR的某些模式中那样)也表示在RGB色度空间中,每个分量都采用相同的抽样频率。
下面用图的形式描述一下Y分量和UV分量。(下面图中叉号表示Y分量,圆表示U和V分量
)
图1 YUV444
YUV 4:4:4采样:每一个Y对应一组UV分量,一个YUV占8+8+8 = 24bits 3个字节
。
码流:Y0 U0 Y1 V1 Y2 U2 Y3 V3
将被映射为下面的四个像素:[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]
4:2:2仍旧是质量相当高的色度抽样方法,大多数高端数字视频格式采用这一比率
图2YUV422
YUV 4:2:2采样,每两个Y共用一组UV分量,一个YUV占8+4+4 = 16bits 2个字节
。
YUV411和YUV420中Y分量和UV分量的比例都是4:1但是取样的方式不同
,看下面的差异YUV 4:1:1 是指水平 Y 取样四个点,UV 各只取样一个点,水平的 Y 和 UV 的取样比例是 4:1,也就是
Y Y Y Y 一个 U 一个 V …
码流:Y0 U0 Y1 Y2 V2 Y3
将被映射为下面的四个像素:[Y0 U0 V2] [Y1 U0 V2] [Y2 U0 V2] [Y3 U0 V2]
图3YUV411
YUV 4:1:1采样,每四个Y共用一组UV分量,一个YUV占8+2+2 = 12bits 1.5个字节
。
4:2:0并不意味着只有Y,Cb而没有Cr分量。它指得是对每行扫描线来说,只有一种色度分量以2:1的抽样率存储。相邻的扫描行存储不同的色度分量,也就是说,如果一行是4:2:0的话,下一行就是4:0:2,再下一行是4:2:0…以此类推。
YUV 4:2:0 是指水平和垂直 Y 各取样两个点,UV 各只取样一个点,水平的取样比例是 2:1,重直的取样比例 2:1,也就是
Y Y
Y Y 一个 U 一个 V …
下面是在MPEG-1 和 MPEG-2 标准中不同,图中可以形象的看出。
图4YUV420(MPEG-1标准)
图5YUV420(MPEG-2标准)
MPEG 最常采用的 YUV 4:2:0 格式
,其 UV 的取样位置,MPEG-1 和 MPEG-2 又不同(上面已经展示了,MPEG-4 是用和 MPEG-2 一样的取样位置)
。
YUV 4:2:0采样,每四个Y共用一组UV分量,一个YUV占8+2+2 = 12bits 1.5个字节
。
在平时的开发中大部分使用的都是YUV420,因为我们对于画面质量和成本有一个平衡,我们的目的是在最小的开销下达到最好的画面质量
。采用YUV420已经能达到我们想要的绝大多数目的,例如平时移动端的视频播放app,直播端的的app等。常见的YUV420分为两种:YUV420P和YUV420SP
。
下面图示以4x4的图片为例子,进行图例展示
。
YUV420P属于plane平面模式,它是YUV标准格式4:2:0。Y , U , V分别在不同平面,主要分为:YU12和YV12
YU12格式在android平台下也叫作I420格式
。
下面是其数据大小计算:
YU12:Y(行×列) + U(行×列/4) + V(行×列/4)
YV12格式与YU12基本相同,不同的是U和V的位置关系,首先是所有Y值,然后是所有V值,最后是所有U值。只要注意从适当的位置提取U和V值,YU12和YV12都可以使用相同的算法进行处理。
YUV420SP格式的图像阵列,首先是所有Y值,然后是UV或者VU交替存储,NV12和NV21属于YUV420SP格式。属于一种two-plane模式
,即Y和UV分为两个plane,但是UV(CbCr)为交错存储在一个plane中。
NV12与NV21类似,也属于YUV420SP格式,NV12存储顺序是先存Y值,再UV交替存储.
android平台下使用相机默认图像格式是NV21属于YUV420SP格式
.
RGB色彩模式是工业界的一种颜色标准,是通过对红®、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一(来自百度百科)。
RGB的所谓“多少”就是指亮度,并使用整数来表示。通常情况下,RGB各有256级亮度,用数字表示为从0、1、2…直到255。也就是说R,G,B各个分量的值可以从0到255.
在Android中RGB我们接触的比较多比如图片的处理时候我们要设置位图Bitmap的格式;字体颜色等。
RGB16(RGB565和RGB555)格式;RGB24格式;RGB32格式
RGB16数据格式主要有二种:RGB565和RGB555。
RGB565使用16位表示一个像素,这16位中的5位用于R,6位用于G,5位用于B。
高字节 低字节
R R R R R G G G G G G B B B B B
可以表示为如下图
可以组合使用屏蔽字和移位操作来得到RGB各分量的值:下面代码可以解决24位与16位相互转换的问题
#define RGB565_MASK_RED 0xF800
#define RGB565_MASK_GREEN 0x07E0
#define RGB565_MASK_BLUE 0x001F
R = (wPixel & RGB565_MASK_RED) >> 11; // 取值范围0-31
G = (wPixel & RGB565_MASK_GREEN) >> 5; // 取值范围0-63
B = wPixel & RGB565_MASK_BLUE; // 取值范围0-31
#define RGB(r,g,b) (unsigned int)( (r|0x08 << 11) | (g|0x08 << 6) | b|0x08 )
#define RGB(r,g,b) (unsigned int)( (r|0x08 << 10) | (g|0x08 << 5) | b|0x08 )
RGB555是另一种16位的RGB格式,RGB分量都用5位表示(剩下的1位不用)。使用一个字读出一个像素后,这个字的各个位意义如下:
高字节 低字节
X R R R R R G G G G G B B B B B (X表示不用,可以忽略)
可以组合使用屏蔽字和移位操作来得到RGB各分量的值:
#define RGB555_MASK_RED 0x7C00
#define RGB555_MASK_GREEN 0x03E0
#define RGB555_MASK_BLUE 0x001F
R = (wPixel & RGB555_MASK_RED) >> 10; // 取值范围0-31
G = (wPixel & RGB555_MASK_GREEN) >> 5; // 取值范围0-31
B = wPixel & RGB555_MASK_BLUE; // 取值范围0-31
RGB24使用24位来表示一个像素,RGB分量都用8位表示,取值范围为0-255。注意在内存中RGB各分量的排列顺序为:BGR BGR BGR…。
下图是一个示意图
RGB32使用32位来表示一个像素,RGB分量各用去8位,剩下的8位用作Alpha通道或者不用。(ARGB32就是带Alpha通道的RGB24。)注意在内存中RGB各分量的排列顺序为:BGRA BGRA BGRA…。
之所以要知道内存中RGB的排列位置关系是因为在RGB和YUV的转化的时候有用
。
接触比较多的就是切图时候的图片和显示字体大小,还有就是为了较小内存消耗选取加载图片时候的Bitmap相关配置。我们看一下
Bitmap.Config.ALPHA_8;//一个像素,只有透明度,占1Byte
Bitmap.Config.ARGB_4444;//一个像素,ARGB分量都是4位,占用2Byte
Bitmap.Config.RGB_565;//一个像素,RGB分量分别使用5位、6位、5位,占2Byte
Bitmap.Config.ARGB_8888;//一个像素,ARGB分量都是8位,占用4Byte
java默认使用大端字节序,c/c++默认使用小端字节序,android平台下Bitmap.config.ARGB_8888的Bitmap默认是大端字节序,当需要把这个图片内存数据给小端语言使用的时候,就需要把大端字节序转换为小端字节序。例如:java层的ARGB_8888传递给jni层使用时,需要把java层的ARGB_8888的内存数据转换为BGRA8888。