0%

YUV

色彩空间

像素格式描述了像素数据存储所用的格式,定义了像素在内存中的编码方式。RGB和YUV为两种经常使用的像素格式。

RGB格式

RGB图像具有三个通道R、G、B,分别对应红、绿、蓝三个分量,由三个分量的值决定颜色;通常,会给RGB图像加一个通道alpha,即透明度,于是共有四个分量共同控制颜色。

RGB用于屏幕图像的展示。

YUV格式

YUV颜色空间是PAL、NTSC、SCEAM三大视频标准使用的颜色空间,主要应用于视频系统。使用YUV色彩空间,后期出现的彩色电视系统和早期的黑白电视系统兼容,黑白电视机可以只处理彩色电信信号中的Y分量,而彩色电视机接收黑白电视信号并显示也没有任何问题。

\(Y'UV\)\(YUV\)\(YC_bC_r\)\(YP_bP_r\)等都可以称为YUV,它们所指涉的范围,常有混淆或重叠的情况。从历史的演变来说,其中\(YUV\)\(Y'UV\)通常用来编码电视的模拟信号,而\(YC_bC_r\)则是用来描述数字的视频信号,适合影片与图片压缩以及传输,例如MPEG、JPEG。 但在现今,YUV通常已经在计算机系统上广泛使用。

YUV用于采集与编码。

  • YUV
    • Y:亮度/灰阶
    • U:色调/色度
    • V:饱和度/浓度
  • \(YP_bP_r\),模拟份量信号/接口
    • P:Paralle,并行
    • b下标:蓝
    • r下标:红
  • \(YC_bC_r\),数字分量信号/接口
    • C,Chroma:色度
    • \(YC_bC_r\)还可指色彩空间,\(YC_bC_r\)色彩空间是YUV色彩空间的缩放和偏移版本。

YUV 在对照片或影片编码时,考虑到人类的感知能力,允许降低色度的带宽。YUV可以通过抛弃色差来进行带宽优化。比如yuv420格式图像相比RGB来说,要节省一半的字节大小,抛弃相邻的色差对于人眼来说,差别不大。

YUV颜色空间和RGB颜色空间可以根据公式相互转换。凡是渲染到屏幕上的东西,都要转换为RGB形式。

标清电视使用标准BT.601: \[ {\left[\begin{array}{l} Y' \\ U \\ V \end{array}\right]\\ = \left[\begin{array}{ccc} 0.299 & 0.587 & 0.114 \\ -0.14713 & -0.28886 & 0.436 \\ 0.615 & -0.51499 & -0.10001 \end{array}\right] \left[\begin{array}{l} R \\ G \\ B \end{array}\right]} \]

\[ {\left[\begin{array}{l} R \\ G \\ B \end{array}\right]\\ = \left[\begin{array}{ccc} 1 & 0 & 1.13983 \\ 1 & -0.39465 & -0.58060 \\ 1 & 2.03211 & 0 \end{array}\right]\\ \left[\begin{array}{l} Y' \\ U \\ V \end{array}\right]} \]

高清电视使用标准BT.709: \[ {\left[\begin{array}{l} Y^{\prime} \\ U \\ V \end{array}\right]=\left[\begin{array}{ccc} 0.2126 & 0.7152 & 0.0722 \\ -0.09991 & -0.33609 & 0.436 \\ 0.615 & -0.55861 & -0.05639 \end{array}\right]\left[\begin{array}{l} R \\ G \\ B \end{array}\right]} \]

\[ {\left[\begin{array}{l} R \\ G \\ B \end{array}\right]=\left[\begin{array}{ccc} 1 & 0 & 1.28033 \\ 1 & -0.21482 & -0.38059 \\ 1 & 2.12798 & 0 \end{array}\right]\left[\begin{array}{l} Y^{\prime} \\ U \\ V \end{array}\right]} \]

对于iOS采集的CMSampleBufferRef,调用CVBufferGetAttachment获取YCbCrMatrix,决定使用BT.601还是BT.709。

采样方式

YUV相比于RGB格式最大的好处是可以做到在保持图像质量降低不明显的前提下,减小文件大小。YUV格式之所以能够做到,是因为进行了采样操作。

YUV图像存储模式与采样方式密切相关。主流的采样方式有三种,YUV4:4:4(YUV444)、YUV4:2:2(YUV422)、YUV4:2:0(YUV420)(所有设备都支持)。这些采样方式,不压缩Y分量,对UV分量的压缩程度不同,这是由人眼的特性决定的,人眼对亮度Y更敏感,对色度UV没有那么敏感,压缩UV分量可以降低数据量,但并不会人眼主观感觉造成太大影响。

YUV后面接的数字就是\(Y\)\(C_b\)\(C_r\)三个分量的比例。

若以以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量,则这三种采样方式如下:

即:

  • YUV4:4:4采样,每一个Y对应一组UV分量。

  • YUV4:2:2采样,每两个Y共用一组UV分量。

  • YUV4:2:0采样,每四个Y共用一组UV分量。

YUV4:4:4

4:4:4,表示完全取样。每个 Y 对应一组 UV 分量。

相邻的4个像素里有4个Y、4个U、4个V。每1个Y使用1组UV分量。如下(每个[]为一个像素点):

1
2
3
4
[ Y U V ]  [ Y U V ]  [ Y U V ]  [ Y U V ]
[ Y U V ] [ Y U V ] [ Y U V ] [ Y U V ]
[ Y U V ] [ Y U V ] [ Y U V ] [ Y U V ]
[ Y U V ] [ Y U V ] [ Y U V ] [ Y U V ]

在这种采样方式下,一个像素点包含的完整的信息。

每个像素大小是3字节(24位),与RGB一致。

YUV4:2:2

4:2:2,表示 2:1 水平取样,垂直完全采样。每两个 Y 共用一组 UV 分量。

相邻的4个像素里有4个Y、2个U、2个V。每2个Y共用1组UV分量。平均算来,一个像素占用的数据宽度为16b,其中Y占8b,U占4b,V占4b。后面存储模式命名中的数字16指的就是16b。平均每个像素大小是2字节,比RGB少⅓。

1
2
3
4
[ Y U ]  [ Y V ]  [ Y U ]  [ Y V ]
[ Y V ] [ Y U ] [ Y V ] [ Y U ]
[ Y U ] [ Y V ] [ Y U ] [ Y V ]
[ Y V ] [ Y U ] [ Y V ] [ Y U ]

在这种采样方式下,还原出一个像素点,需要相邻的两个像素点数据,如下:

1
[ Y U ]  [ Y V ]

YUV4:2:0

4:2:0,表示 2:1 水平取样,垂直 2:1 采样。

4:1:0并不意味着只有\(Y\)\(C_b\)两个分量,而没有\(C_r\)分量。实际指的是对每行扫描线来说,只有一中色度分量,相邻的扫描线存储不同的色度分量。

相邻的4个像素里有4个Y、2个U、0个V,或4个Y、2个V,0个U。每4个Y共用1组UV分量。平均算来,一个像素占用的数据宽度为12bit,其中Y占8bit,U占2bit,V占2bit。后面存储模式命名中的数字12指的就是12b。平均每个像素是12b,比RGB少½。

1
2
3
4
[ Y U ]  [ Y ]  [ Y U ]  [ Y ]
[ Y V ] [ Y ] [ Y V ] [ Y ]
[ Y U ] [ Y ] [ Y U ] [ Y ]
[ Y V ] [ Y ] [ Y V ] [ Y ]

在这种采样方式下,还原出一个像素点,需要相邻的四个像素点数据,如下:

1
2
[ Y U ]  [ Y ]
[ Y V ] [ Y ]

\[ YUV4:2:0 数据量 = Y × 1.5 = RGB ÷ 2 \]

存储格式

在同一采样模式下,根据分量元素排列顺序的不同,又分为不同的存储模式。

  • packed,紧缩格式(packed formats):将 Y、U、V 值存储成一个 Macro Pixels 数组,和 RGB 的存放方式类似。

    • 内存中排列形式类似:YVYUYVYUYVYUYVYU...。

    • 在具体的存储模式命名中,packed格式不带后缀P。

    • 适合YUV 4:4:4

  • planar,平面格式(planar formats):将 Y、U、V 3个分量分别存放在不同的矩阵中(3个字节数组)。

    • 内存中排列形式类似:YYYYYY...,UUUUUU...,VVVVVV...。

    • 在具体的存储模式命名中,planar格式带后缀P。

    • 适合I420(YUV420p)、YV12(YUV420p)

  • semi-planar,将Y、U、V三个分量放在2个矩阵(平面)中(2个字节数组)。Y占用一个平面,UV共用一个平面。

  • 内存中排列形式类似:YYYYYY...,UVUVUV...。

    • 在具体的存储模式命名中,semi-planar格式带后缀SP。
  • 适合NV12(YUV420sp)、NV21(YUV420sp)

像素格式

YUV422

内存分布:YUYV、YVYU、UYVY、VYUY

这四种格式每一种又可以分为2类(packed和planar),以YUYV为例,一个6*4的图像的存储方式如下:

1
2
3
4
5
6
7
8
9
Y Y Y Y Y Y                   
Y Y Y Y Y Y
Y Y Y Y Y Y
Y Y Y Y Y Y
U U U U U U Y U Y V Y U Y V Y U Y V
U U U U U U Y U Y V Y U Y V Y U Y V
V V V V V V Y U Y V Y U Y V Y U Y V
V V V V V V Y U Y V Y U Y V Y U Y V
- Planar - - Packed -

YUV420

  • YUV420p:I420、YV12,planar,分别存储在三个字节数组中。
  • YUV420sp:NV12、NV21,semi-planar,Y存储在一个数组中,UV存储在一个数组中。

同样,对于一个6*4的图像,这四种像素格式的存储方式如下:

1
2
3
4
5
6
7
Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y      Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
U U U U U U V V V V V V U V U V U V V U V U V U
V V V V V V U U U U U U U V U V U V V U V U V U
- I420 - - YV12 - - NV12 - - NV21 -

占用字节数

YUV420图像占用字节数为 :

1
size = width * height + (width * height) / 4 + (width * height) / 4 = width * height * 1.5 // 刚好是 RGB 的一半

RGB格式的图像占用字节数为:

1
size = width * height * 3

RGBA格式的图像占用字节数为:

1
size = width * height * 4

YUV数据访问

对于一个YUV格式存放的文件,可以直接读取并分离成Y、U、V各个分量的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 读取YUV420P的文件,每个Y、U、V值都是用一个字节存储,所以使用刚好是一字节大小的char表示
int yuv_size = w * h * 3 / 2;
unsigned char *pic = (unsigned char *)malloc(yuv_size);
fread(pic, 1, yuv_size, fyuv);

int written_byte, writting_byte = 0;
// Y
writting_byte = w * h;
fwrite(pic + written_byte, 1, writting_byte, fy);
written_byte += writting_byte;
// U
writting_byte = w * h / 4;
fwrite(pic + written_byte, 1, writting_byte, fu);
written_byte += writting_byte;
// V
writting_byte = w * h / 4;
fwrite(pic + written_byte, 1, writting_byte, fv);
written_byte += writting_byte;

// 对于YUV444P的分离也是类似,只是:
// yuv_size = w * h * 3
// writting_byte = w * h

对于YUV格式数据变成灰度图也很简单,只需要把U、V值都置空即可:

1
2
3
4
5
6
7
8
// 读取YUV420P的文件
int yuv_size = w * h * 3 / 2;
unsigned char *pic = (unsigned char *)malloc(yuv_size);
fread(pic, 1, yuv_size, fyuv);

// Gray
memset(pic + w * h, 0, w * h / 2);
fwrite(pic, 1, yuv_size, fyuv);

iOS相机支持输出图片格式

  • 420v

    • kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange

    • 表示输出的视频格式为NV12(YUV420sp)

    • 范围:(luma = [16,235], chroma = [16,240])

  • 420f

    • kCVPixelFormatType_420YpCbCr8BiPlanarFullRange

    • 表示输出的视频格式为NV12(YUV420sp)

    • 范围:(luma = [0,255], chroma = [1,255])

  • BGRA

    • kCVPixelFormatType_32BGRA
    • 输出的是BGRA的格式

Android从摄像头采集的预览数据一般都是NV21,iOS一般采集的数据都是NV12。

参考资料

欢迎关注我的其它发布渠道