色彩空间
像素格式描述了像素数据存储所用的格式,定义了像素在内存中的编码方式。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 | [ 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 | [ Y U ] [ Y V ] [ Y U ] [ Y V ] |
在这种采样方式下,还原出一个像素点,需要相邻的两个像素点数据,如下:
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 | [ Y U ] [ Y ] [ Y U ] [ Y ] |
在这种采样方式下,还原出一个像素点,需要相邻的四个像素点数据,如下:
1 | [ Y U ] [ 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 | Y Y Y Y Y Y |
YUV420
- YUV420p:I420、YV12,planar,分别存储在三个字节数组中。
- YUV420sp:NV12、NV21,semi-planar,Y存储在一个数组中,UV存储在一个数组中。
同样,对于一个6*4的图像,这四种像素格式的存储方式如下:
1 | Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y |
占用字节数
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 | // 读取YUV420P的文件,每个Y、U、V值都是用一个字节存储,所以使用刚好是一字节大小的char表示 |
对于YUV格式数据变成灰度图也很简单,只需要把U、V值都置空即可:
1 | // 读取YUV420P的文件 |
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。
参考资料
https://juejin.im/post/5a572730f265da3e2c3803ad