欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

深入解析YUV格式的原理和应用

最编程 2024-08-15 16:16:40
...

什么是YUV

YUV是一种色彩空间的模型,基于YUV格式的颜色编码,是流媒体的常用编码方式。如我们平时看到的彩色电视、手机摄像头图像采样等,其原始像素数据YUV格式。


YUV的优点

了解YUV的优点,不妨先简单介绍一下我们熟悉的RGB格式。


RGB

  • 色彩表示

    RGB就是所谓的三原色,即分别为:红(R)、绿(G)、蓝(B)。将这三种颜色以不同比例进行混合,就能够得到多种不同的颜色出来,平时生活中,水彩绘画可以印证这一点。而在计算机中,RGB每一个分量的颜色程度以0~255的范围进行表示,例如R分量的值为0,则表示没有任何一点红色色彩,R分量的值为255,则表示红色色彩程度达到最大,G分量和B分量同理。因此所谓的红色,就可以用255,0,0来表示,对应的,绿色为0,255,0,蓝色为0,0,255。那么如果RGB这三个分量分别取不同的值,就可以得到不同的颜色效果出来了,如下图:

tiaoseban

而且,这只是RGB能表示的部分颜色,用数学的排列组合来算的话,RGB能表示:255 * 255 * 255 = 16581375 种 颜色,只不过有些颜色其三个分量的程度值比较接近,肉眼难以分辨出来而已。

  • 存储空间

    我们平时所看到的图像,都是由一个个像素点经过一定的排列组成的,每一个像素点都有红、绿、蓝三种原色。在计算机中,每一种原色占用8个bit大小,因此,每个像素点就占用24个bit大小,即3个byte(1byte = 8bit)。上面提到的计算机中的颜色值范围是0 ~ 255,就是因为一个原色分量占用8bit,2^8 = 256,从0开始,到255,刚好256,255的值就这么来的。

    那么对于一张1920 * 1080的图片,如果采用RGB编码格式,这一张图片加载到内存并在设备中显示时,所占用的内存大小就是:

    1920 * 1280 * 3 /1024 / 1024 = 7.03125M

    这还只是针对JPG图片,如果是PNG图片,还会多出一个透明分量,同样分辨率的图片,所占用的内存会更大一些。


那YUV又是怎样一种格式呢?


YUV

YUV编码采用了明亮度和色度来表示每一个像素的颜色,其中:

Y表示明亮度(Luminance、Luma),描述的是灰度值。

U、V表示色度(Chrominance 或 Chroma),分别描述的是色调和饱和度。

而平时还能看到另外一种类似YUV的编码格式,即YCbCr,YCbCr 其实是 YUV 经过缩放和偏移的翻版。其中 Y 与 YUV 中的 Y 含义一致,Cb、Cr 同样都指色彩,只是在表示方法上不同而已。YCbCr 其中 Y 是指亮度分量,Cb 指蓝色色度分量,而 Cr 指红色色度分量。

相比于RGB,YUV的优势在哪呢?

  • 兼容性

    对于YUV所表示的图像,Y和UV分量是分离的。如果只有Y分量,没有UV分量,图像就体现为黑白色彩,即所谓的黑白图,灰度图,很早以前的的黑白电视机就是只有Y分量。后来出现的彩色电视机也采用了YUV格式,解决了与黑白电视机的兼容问题,使得黑白电视机也能接收彩色电视的图像信号。

  • 编码后的内存占用更小

    经过研究表明,我们人眼对色度的敏感程度低于对亮度的敏感程度。主要原因是视网膜杆细胞多于视网膜锥细胞,其中的视网膜杆细胞的作用就是识别亮度,视网膜锥细胞的作用就是识别色度。因此,人眼对亮度的分辨要比对颜色的分辨要精细。

    利用这个原理,在进行色彩采样的时候,就可以把色度信息适当减少,这样子省下了部分编码存储空间,同时最终的显示效果,人眼也不太容易察觉。

    因此,YUV在编码时,每一个像素点的采样并不是都需要包含Y、U、V三个分量,可以每个Y分量,都对应自己的UV分量,也可以几个Y分量共享UV分量。当然,这里的共享,是指解码YUV进行像素渲染时的共享。而不同的Y、UV组合,对应了几种不同的YUV采样格式。


YUV采样格式


主流的YUV采样方式有如下三种:

  • YUV444

  • YUV422

  • YUV420

其中,三个数字分别代表在采样时,Y、U、V分量的采样比例。但是有一点要理解清楚,就是U和V是一个色度分量整体,我们所说的色度分量是指UV,只不过色度又分为色调(U)和饱和度(V),理解中这个,有助于理解下面说的采样方式。

YUV444

YUV444表示Y、U、V三个分量的采样比例相同,即每个像素的YUV分量信息完整,都是8bit,每个像素占用3个byte。其像素采样示例图如下:

注:× 表示Y分量,○表示UV分量。

YUV444_2

对于YUV444格式有:

若原始的四个像素数据为:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]

采样编码时的码流数据为::Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3

根据采样数据码流最终渲染为:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]

可见,YUV444采样方式得到的图片大小跟RGB是一样的。

YUV422

YUV420表示UV分量的采样率是Y分量的一半,采样示例图如下所示:

YUV422

对于YUV422格式有:

若原始的四个像素数据为:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]

采样编码时的码流数据为:Y0 U0 Y1 V1 Y2 U2 Y3 U3

根据采样数据码流最终渲染为:[Y0 U0 V1][Y1 U0 V1][Y2 U2 V3][Y3 U2 V3]

其中,每采样一个像素点,都会采样Y分量,而每采样两个Y,就采样一个色度分量UV,其中U来自第一个像素,V来自第二个像素,最终渲染时,第一个像素和第二个像素共享U、V分量,以此类推,从而减小了内存占用空间。

如一张1920 * 1280 分辨率的图片,若YUV422采样,那么它占用的内存大小是:

yuv422_size_3

可见,YUV422比RGB节省了三分之一的内存空间。

YUV420

YUV420 并不意味着不采样V分量,它的采样方式是:每采样一个像素点,都会采样Y分量,而每采样两个Y,就采样一个色度分量UV的一半,即要么只采样U,或者只采样V,而不像YUV422那样每两个Y就有一个完整的UV。对于每条扫描线来说(可以理解为每一行像素),只有一种色度分量会以2:1的比例被采样,而相邻的扫描行采样不同的色度分量。例如,若第一行是4:2:0采样,下一行就是4:0:2采样,再一行就是4:2:0采样,以此类推。YUV420采样示例图如下:

YUV420

对于YUV420格式有:

若原始的四个像素数据为:

[Y0 U0 V0][Y1 U1 V1][Y2 U2 V2][Y3 U3 V3] [Y5 U5 V5][Y6 U6 V6][Y7 U7 V7][Y8 U8 V8]

则采样编码时的码流数据为:

Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8

根据采样数据码流最终渲染为:

[Y0 U0 V5][Y1 U0 V5][Y2 U2 V7][Y3 U2 V7] [Y5 U0 V5][Y6 U0 V5][Y7 U2 V7][Y8 U2 V7]

其中,每采样一个像素点,都会采样Y分量,而U、V分量则会隔行(和Y)按照2:1的比例进行采样,最终的渲染时,第一行的第一和第二个像素,会跟第二行的第一第二个像素共享UV分享,即四个像素一起共享了一个UV分量(Y:UV=4:1),以此类推,相比RGB,节省了一半的内存空间。

如一张1920 * 1280 分辨率的图片,若YUV420采样,那么它占用的内存大小是:

yuv420_size


YUV的存储格式


除了YUV的采样格式,我们还需要了解YUV的数据存储格式。

YUV的数据存储格式有三种:

  • planar

  • packed

  • semi-planar

planar:先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。

packed:每个像素点的Y、U、V是连续交错存储的。

semi-planar:先存Y,再存UV。

因为不同的采样格式和存储格式,就会产生出多种YUV编码方式,这里简单介绍平时常见的YUV422和YUV420编码方式。


YUV422


YUYV(YUY2)- packed

每个像素都采集 Y 分量,每隔一个像素采集它的 UV 分量。

yuyv

UYVY - packed

UYVY跟YUYV差不多一样,区别只是UV的排列顺序相反。

uyvy

YUV422P

YUV422P属于YUV422采样,它的存储格式是planar,分YU16和YV16两种:

yu16_2

yv16

YUV422SP

YUV422SP 属于YUV422采样,存储格式是planar,分为NV16、NV61:

nv16
nv61_2

YUV420


YUV420P

YUV420P属于YUV420采样,planar存储,分为YU12、YV12:

yu12
yv12

YUV420SP

YUV420SP属于YUV420采样,planar存储,分为NV12、NV21:

nv12_2

nv21


这里对上面的图示和分类简单做一下说明,便于理解:

其实NV系列,都是semi-planar存储;

这里的NV12表示正常的顺序,即UV排列,先U,后V;

而NV21表示相反的顺序,即VU排列,先V,后U;

同样,NV16和NV61的区别也仅仅是UV的顺序不一样而已。

而这里的12个16表示的其实是一个像素所占的bit,以NV12为例,其表示一个像素栈12bit,其中Y是固定的8bit,剩下的4bit,U占2bit,V占2bit,实际上就是YUV420采样格式,具体点的,就是YUV420SP;

同样,NV16,表示一个像素占用16bit,其中Y是8bit,U是4bit,V是4bit,实际上就是YUV422采样格式,具体的就是YUV422SP。

另外,上述的示例图,只是以图片宽x高的形式进行展现,实际编码到文件时,是不存在换行这种概念的,比如这个420P的图:

yu12

编码到文件时,字节流将是这样:

Y0Y1Y2Y3Y4Y5Y6....Yn U0U1U2U3U4U5U6...Um V0V1V2V3V4V5V6...Vk

当我们通过代码得到字节流时,还需要自己控制好相关偏移量,以正确处理图像像素数据。


YUV分量抽取示例图


说了这么多概念的东西,来点实在的,通过一些效果图的展示,有助于更好了解YUV格式编码。

YUV420P效果图

yuv420p效果图

YUV444P效果图

yuv444p效果图

相关文件大小信息

yuv_size

关于文件大小,这里简单解释一下:

1、jpg文件是原图,但是它的文件大小只有52K,而.yuv文件却比jpg文件大很多,这是因为jpg是图像压缩格式,yuv是图像的原始像素数据,未经过压缩的。jpg要经过像素数据编码存储而得,而我们用电脑预览jpg图片时,是图片软件这个东西帮我们对jpg压缩数据进行的解压,或者叫解码,然后重新转换回像素数据,再渲染显示出来。

2、从图示的文件大小信息可以看出,YUV420采样后的存储文件大小,确实是比YUV444采样的存储文件要小,而且,恰好,对于同一张图片,YUV420的文件大小,刚好是YUV444的一半。

3、从图示的文件大小信息可以看出,把YUV完整数据,分割成Y、U、V分量三种数据得到的文件大小,三者之和,刚好就是对应原始YUV数据的文件大小。


关于源码

源码已放GitHub上,有兴趣的可以pull下来~



关于本文,如果有描述不正确的地方,欢迎留言指出~


THE END