[原创] YUV 基础知识

前言

  Android 从 5.0(API 21, 2014年发布)开始,Google 已经将原来的 Camera 类废弃了,改用全新设计的功能更加强大的 Camera2,并且开始主推新的格式 YUV420。因此我们有必要先学习下有关 YUV 的基础知识。

  Android 5 之前的版本,Camera Preview 支持的格式是包括 NV21(属于 YUV420SP), YV12(属于 YUV420P),NV16,默认图像格式是 NV21,官方强烈建议使用 NV21 或 YV12。而对于 Andriod 5 及之后版本,支持全新的 YUV420Flexible 格式,配套 YUV_420_888

  YUV420Flexible 并是一种具体的格式,而是一类 YUV 格式,包括 I420(属于 YUV420P) 还有旧版 Camera 支持的 NV21YV12YUV420Flexible 格式的最大优点就是速度快。在实时预览时,该格式至少可以达到 30 FPS。

YUV 简介

  YUV 是一种亮度信号 Y 和色度信号 U、V 是分离的色彩空间,它主要用于优化彩色视频信号的传输,使其向后相容老式黑白电视。与 RGB 视频信号传输相比,它最大的优点在于只需占用极少的频宽( RGB 要求三个独立的视频信号同时传输)。其中“Y”表示明亮度(Luminance 或 Luma),也就是灰阶值;而“U”和“V”表示的是色度、浓度(Chrominance 或 Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。

  使用 YUV 的优点有两个:

  1. 彩色 YUV 图像转黑白 YUV 图像转换非常简单,这一特性用在于电视信号上;
  2. YUV 是数据总尺寸小于 RGB 格式;

YUV 与 RGB 区别

  YUV 的存储中与 RGB 格式最大不同在于,RGB 格式每个点的数据是连继保存在一起的。即 R,G,B 是前后不间隔的保存在2~4byte 空间中。而 YUV 的数据中为了节约空间,U,V分量空间会减小。每一个点的Y分量独立保存,但连续几个点的 U,V 分量是保存在一起的,通常人的肉眼察觉不出。

YUV 格式

  YUV 格式分为两种类型:

  • 紧缩格式(Packed 格式):Packed 类型是将 Y、U、V 分量存在在同一个 Macro Pixels 数组中,和 RGB 的存放方式类似。每个像素点的 Y、U、V 是连续交错存储的。
  • 平面格式(Planar 格式):将 Y、U、V 分量分别存放到三个独立的数组中,先连续存储所有像素点的 Y,紧接着存储所有像素点的 U,最后是所有像素点的 V。

  Packed 格式中,YUV 是混合在一起的,对于 YUV4:4:4 格式而言,用紧缩格式很合适的,因此就有了 UYVY、YUYV 等。
  Planar 格式中,每 Y 分量,U 分量和 V 分量都是以独立的平面组织的。常见平面格式(planar format)有YU12(Android 平台叫 I420)、YV12、IYUV 等。

  为节省带宽起见,大多数 YUV 格式平均使用的每像素位数都少于24位。主要的抽样(subsample)格式有YCbCr 4:2:0、YCbCr 4:2:2、YCbCr 4:1:1和YCbCr 4:4:4。YUV 的表示法称为 A:B:C 表示法,每个像素由一组 YUV 组成:

  • 4:4:4 表示完全取样。每一个Y对应一组U/V分量。每像素(即一组YUV)占3个字节(8+8+8=24bits)
  • 4:2:2 表示 2:1 的水平取样(即水平方向隔一行采样一次U/V分量),垂直完全采样。每2个Y共用一组UV分量。每像素(即一组YUV)占2个字节(8+4+4=16bit)
  • 4:2:0 表示 2:1 的水平取样(即水平方向隔一行采样一次U/V分量),垂直 2:1 采样(即垂直方向隔一行采样一次U/V分量)。每4个共用一组UV分量。每像素(即一组YUV)占1.5个字节(8+2+2=12bits)或2个字节(8+4+4=16bits)
  • 4:1:1 表示 4:1 的水平取样,垂直完全采样。

  最常用的 YUV420PYUV420SP 都是按 YUV 4:2:0 的方式采样的。而且 DVD-Video 也是以 YUV 4:2:0 的方式采样的,也就是我们俗称的 I420(属于 YUV420P),YUV4:2:0 并不是说只有 U(即Cb), V(即Cr)一定为0,而是指 U:V 互相援引,时见时隐,也就是说对于每一个行,只有一个 U 或者只有一个V 分量。如果一行是 4:2:0 的话,下一行就是 4:0:2,再下一行是 4:2:0,以此类推。至于其它常见的 YUV 格式有 YUY2、YUYV、YVYU、UYVY、AYUV、Y41P、Y411、Y211、IF09、IYUV、YV12、YVU9、YUV411、YUV420等。

YUV420

  首先说明下,大家从网查关于 YUV 的资料时会看到很多格式,每个人的写法和叫法也不太一样。这对于那些刚开始接触 YUV 的人来讲很是迷糊,我刚开始就这样。不过看了很多官方文档后,慢慢的明白了那些网上关于 YUV 具体格式的叫法也对也不对,主要是不准确。我这边先总结一下术语

  YUV420 并不是一种具体的格式,而是代表一组格式。根据颜色数据的存储顺序不同,又分为了多种不同的格式。其中最常用的 YUV420 分为两大类:YUV420P(非具体格式,也代表一组格式) 和 YUV420SP(非具体格式,也代表一组格式):

  • YUV420Planar(即 YUV420P):包含 YU12(Android 平台叫作 I420),YV12。这些才是具体的 YUV420 格式。YU12 与 YV12 基本相同,仅 U/V 顺序不同,详见后文。
  • YUV420SemiPlanar(即 YUV420SP):包含 NV12,NV21。这些才是具体的 YUV420 格式。NV12 和 NV21 基本相同,仅 U/V 顺序不同,详见后文。
  • YUV420PackedPlanar
  • YUV420PackedSemiPlanar

注意:只有确定了具体格式,才能知道其准确的数据存储方式。
说明:大家上网查阅资料时会发现,网上很多文章将 YUV420P 等同于 I420(即 YU12),将 YUV420SP 等同于 NV12 了。看了上面的介绍,读者应该明白了,这种说法其实是不准确的。YUV420P 和 YUV420SP 代表的是一组格式,并不是某一种具体格式。而 I420(即 YU12),NV21 等才是具体的格式。读者在查阅网上资料时,需要注意该问题。

  上述这些格式实际存储的信息是完全一致的。举例来说,对于 4x4 的图片,在 YUV420 下,任何格式都有16个 Y 值,4个 U 值和4个 V 值,不同格式只是 Y、U 和 V 的排列顺序变化。例如:I420YUV420Planar 的一种)为 YYYYYYYY UUVVNV21YUV420SemiPlanar 的一种)则为 YYYYYYYY VUVU。也就是说,YUV420 是一类格式的集合,并不能完全确定颜色数据的存储顺序。只有确定了具体格式,才能知道其准确的数据数据存储方式。

  I420,YV12(属于 YUV420P):Y、U 和 V 三个分量的数据分别保存在三个 Plane 中。4 个 Y 分量共享一个 U/V 分量。
  NV12,NV21(属于 YUV420SP):是一种 two-plane 模式。即 Y 和 U/V 分为两个 Plane, Y 占用一个 Plane,U/V 共用一个 Plane 且 U/V 数据交错存储。同样是 4 个 Y 分量共享一个 U/V 分量。

  前面已经简要的介绍了 YUV。这里主要简述下 YUV420。YUV 根据 U 和 V 采样数目的不同,分为 YUV444、YUV422 和 YUV420 等,而 YUV420 表示的就是每个像素点有一个独立的亮度表示,即 Y 分量;而色度,即 U 和 V 分量则由每4个像素点共享一个(即每 2x2 个 Y 分量共享一个 U,V 分量)。也就是说,Y 分量的数量与图像的总像素数是一致的,而 U 和 V 分量数是 Y 分量数的 1/4。举例来说,对于 6x4 的图片,在 YUV420 下,有24个 Y 值,6个 U 值和6个 V 值。见下图:
  
YUV420 Single Frame

  通过上图可知,Y1,Y2,Y7,Y8 这个 2x2 的矩阵(4个像素)共享一个 U1,V1,也就是说每一个 U 和 V 对应 4 个像素。
  将上图数据转换成数组后的存储形式如下:
  
Position In Byte Stream

  根据上图可知(以下描述中的 x 代表图像的宽度,y 代表图像的高度):

  • Y 分量的起始索引值为 0。
  • U 分量的起始索引值为 x y(以当前示例的话,U 分量起始索引值为 6 4 = 24)。
  • V 分量的起始索引值为 x y + (x y)/4(以当前示例的话,V 分量起始索引值为 6 4 + (6 4) / 4 = 30)。
YUV420 具体格式 所属类型 说明
I420(YU12) YUV420P 所有 Y 在最面,之后是所有的 U 在前,最后是 所有的 V。例如:YYYYYYYY UUVV
YV12 YUV420P 和 I420 一样,不同之处是 U 与 V 数据反转。V 在前,U 在后。即 YV 表示 Y 后面是 V。12 表示 12 bit。例如:YYYYYYYY VVUU。YUV420P 与 YV12 可以使用相同的算法进行处理。许多重要的编码器都采用YV12空间存储视频:MPEG-4(x264XviDDivX),DVD-Video存储格式MPEG-2,MPEG-1以及MJPEG。
NV12 YUV420SP U/V 交错存储,U 在前,V 在后。例如:YYYYYYYY UVUV。(注意:网上很多文章都写错了,将 NV12 和 NV21 的存储形式写反了。)
NV21 YUV420SP Android Camera 的标准图像格式(即老版的 Camera,非 Camera2),与 NV12 类似,仅U/V顺序不同。U/V 也是交错存储,不过 V 在前, U在后。例如:YYYYYYYY VUVU。(注意:网上很多文章都写错了,将 NV12 和 NV21 的存储形式写反了。)

  再次提醒大家,网上很多文章都将 NV12 和 NV21 的存储形式写反了,估计是一人写错,大家一转载导致错误被广大了。NV12 是 YYYYYYYY UVUV 这样存储的(不是 YYYYYYYY VUVU),NV21 是 YYYYYYYY VUVU 这样存储的(不是 YYYYYYYY UVUV)。而且 Android 官方文档也写明了:

NV21: YCrCb format used for images, which uses the NV21 encoding format.

  看到了吧,NV21 是 YCrCb 格式,也就是 V 在前,U在后的顺序。感兴趣的还可以查阅下 LinuxTV 的官方文档对 NV12 及 NV21 的说明。

  总结:  

  • YUV420P: U/V 数据是连续存储的,该类型下的具体格式之间的差别仅在于 U/V 的存储顺序不同。
  • YUV420SP: U/V 数据是交替存储的,该类型下的具体格式之间的差别仅在于 U/V 的存储顺序不同。
  • YUV420 占用的内存空间大小是:width height 3 / 2 单位:byte
    • Y = width * height
    • U = Y / 4
    • V = Y / 4

Android YUV420 分量

  Android 对从 Camera2 返回的 YUV420 数据有自己的处理,从而让我们使用起来更方法。强烈建议大家阅读下我写的另一篇文章“Android 使用 Camera2 获取实时摄像头数据并实时编码成 H264”一定会帮助到你。(我可是花了很长很长时间来学习并撰写文章的,都是干货!)

参考文献

坚持原创及高品质技术分享,您的支持将鼓励我继续创作!