你好,我是李江。今天我们一起来聊一聊图像的颜色空间。
图像的颜色空间是图像和视频技术里面的一个非常重要的知识点,在图像处理、视频编码等技术中你会经常遇到这个概念。
而至于它为什么重要,其实也很好理解。在现实世界中,我们的眼睛每天看到的颜色是千变万化的。为了能够更方便地表示和处理这些颜色,不同应用领域就建立了多种不同的颜色空间,主要包括RGB 、YUV、CMYK 、 HSI等(后面两种和这门课程没有关系,因此这里我们不再介绍)。
在视频技术中,我们经常碰到的颜色空间有两种:RGB和YUV,接下来我们就逐一看一看。
RGB相对比较简单。顾名思义,它就是指图像的每一个像素都有R、G、B三个值。RGB是我们平常遇到最多的一种图像颜色空间,比如摄像头采集的原始图像就是RGB图像,且显示器显示的图像也是RGB图像。
一般来说,我们的RGB图像,每一个像素都是分别存储R、G、B三个值,且三个值依次排列存储。比如对于一张8bit位深的RGB图,每个值占用一个字节。但是,需要注意的是RGB图像像素中R、G、B三个值并不一定是按R、G、B顺序排列的,也有可能是B、G、R顺序排列。
比如OpenCV就经常使用BGR的排列方式来存储图像。所以在存储和读取RGB图像的时候需要稍微注意一下。RGB和BGR的存储方式如下图所示:
虽然RGB比较简单,同时在图像处理的时候也经常会用到。但是在视频领域,我们更多地是使用YUV颜色空间来表示图像的。这是因为R、G、B三个颜色是有相关性的,所以不太方便做图像压缩编码。那YUV究竟是怎么表示图像的呢?它又是如何存储在内存当中的呢?我们接下来就来揭开它的“面纱”。
YUV跟RGB类似,也是一种颜色空间,但其种类会更多更复杂些,所以接下来我们会花大量的篇幅去讲解它。
YUV最早主要是用于电视系统与模拟视频领域。现在视频领域基本都是使用YUV颜色空间。
跟RGB图像中R、G、B三个通道都跟色彩信息相关这种特点不同,YUV图像将亮度信息Y与色彩信息U、V分离开来。Y表示亮度,是图像的总体轮廓,称之为Y分量。U、V表示色度,主要描绘图像的色彩等信息,分别称为U分量和V分量。这样一张图像如果没有了色度信息U、V,只剩下亮度Y,则依旧是一张图像,只不过是一张黑白图像。这种特点有什么好处呢?
在以前,世界上只有黑白电视机,每一帧电视画面都是黑白的,没有色彩信息。当然黑白电视机也不支持显示彩色图像。后来随着技术的发展,出现了彩色电视机,每一帧画面都是有颜色信息的,那当然我们可以使用RGB、YUV等颜色空间来表示一帧图像。
但是考虑到兼容老的黑白电视机,如果使用RGB表示图像,那么黑白电视机就没办法播放。这是因为R、G、B三个通道都是彩色的,而Y、U、V就可以。因为黑白电视机可以使用Y分量,Y分量就是黑白图像,而且包含了图像的总体轮廓信息,只是没有色彩信息而已。
好了,讲了YUV的起源之后,我们来讲讲YUV颜色空间最重要的知识点,那就是YUV的类型和存储方式。
YUV主要分为YUV 4:4:4、YUV 4:2:2、YUV 4:2:0这几种常用的类型。其中最常用的又是YUV 4:2:0。这三种类型的YUV主要的区别就是U、V分量像素点的个数和采集方式。
YUV 4:4:4就是每一个Y就对应一个U和一个V;而YUV 4:2:2则是每两个Y共用一个U、一个V;YUV 4:2:0则是每四个Y共用一个U、V。我们可以通过图片来清晰地看一下三种YUV类型的区别。具体如下图所示:
总的来说:
YUV 4:4:4这种类型非常简单,所以存储的方式也非常简单。
那YUV 4:2:2和YUV 4:2:0这种共用U、V分量的情况,应该在内存中怎么存储呢?下面我就来为你介绍一下。
YUV存储方式主要分为两大类:Planar和Packed两种。Planar格式的YUV是先连续存储所有像素点的Y,然后接着存储所有像素点的U,之后再存储所有像素点的V,也可以是先连续存储所有像素点的Y,然后接着存储所有像素点的V,之后再存储所有像素点的U。Packed格式的YUV是先存储完所有像素的Y,然后U、V连续的交错存储。
下面我们就来看看每一种YUV类型的存储方式是怎么样的。
这种类型的YUV非常简单,因为每一个Y对应一个U、一个V,所以存储的方式也非常简单。例如,4 x 2像素的YUV 4:4:4存储图如下图所示:
可以看到,YUV 4:4:4和RGB图像存储之后的大小是一样的。如果是8bit图像,就是每一个像素点需要占用3个字节。
这种类型的YUV稍微复杂些,每左右两个像素的Y共用一个U和一个V。存储方式主要有以下4种类型。
该类型是Planar格式,先存储完Y,再存储U,之后存储V。例如,4 x 2像素的YU16存储图如下图所示:
该类型也是Planar格式,先存储完Y,再存储V,之后存储U。例如,4 x 2像素的YV16存储图如下图所示:
这种类型是Packed格式,先存储完Y,之后U、V连续交错存储。例如,4 x 2像素的NV16存储图如下图所示:
这种也是Packed格式,与NV16不同,这种格式是先存储完Y,之后V、U连续交错存储。例如,4 x 2像素的NV61存储图如下图所示:
可以看到,4 x 2像素的YUV 4:2:2只需要16个字节,而RGB图像则需要24个字节。也就是说,如果是8bit图像,那么RGB每一个像素需要3个字节,而YUV 4:2:2只需要2个字节。
这是最常见也是最常用的YUV类型。通常视频压缩都是YUV 4:2:0格式的。它是每上、下、左、右4个像素点共用一个U和一个V。存储方式主要分为以下4种。
这种类型是Planar格式,先存储完Y,再存储U,之后存储V。例如,4 x 4像素的YU12存储图如下图所示:
该类型也是Planar格式,先存储完Y,再存储V,之后存储U。例如,4 x 4像素的YV12存储图如下图所示:
这种类型是Packed格式,先存储完Y,之后U、V连续交错存储。例如,4 x 4像素的NV12存储图如下图所示:
这种也是Packed格式,与NV12不同,这种格式是先存储完Y,之后V、U连续交错存储。例如,4 x 4像素的NV21存储图如下图所示:
可以看到,4 x 4像素的YUV 4:2:0只需要24个字节相比RGB图像需要48个字节,存储的大小少了一半。也就是说,如果是8bit图像,RGB每一个像素需要3个字节。而YUV 4:2:0只需要1.5个字节。
好了,以上就是YUV颜色空间的主要类型和存储方式。这里我用表格给你总结了一下。
我们刚才说到,一般来说,采集到的原始图像、给显示器渲染的最终图像都是RGB图像,但是视频编码一般用的是YUV图像。那么这中间一定少不了两者的相互转换。那RGB如何转到YUV呢?YUV又如何转到RGB呢?
在讲转换之前,我们先了解Color Range这个东西。对于一个8bit的RGB图像,它的每一个R、G、B分量的取值按理说就是0~255的。但是真的是这样的吗?其实不是的。这里就涉及到Color Range这个概念。Color Range分为两种,一种是Full Range,一种是Limited Range。Full Range的R、G、B取值范围都是0~255。而Limited Range的R、G、B取值范围是16~235。
了解了Color Range之后,我们怎么规范YUV和RGB之间的互转呢?其实这也是有标准的,目前的标准主要是BT601和BT709(其实还有BT2020,我们这里不展开讲)。简单来讲,BT709和BT601定义了一个RGB和YUV互转的标准规范。只有我们都按照标准来做事,那么不同厂家生产出来的产品才能对接上。BT601是标清的标准,而BT709是高清的标准。
下面我们来看看这两种标准分别在Full Range和Limited Range下的RGB和YUV之间的转换公式吧。具体如下图所示:
从上图我们可以看到每种标准下不同Color Range的转换公式是不同的。所以在做RGB往YUV转换的时候我们需要知道是使用的哪个标准的哪种Range做的转换,并告知对方。这样对方使用同样的标准和Range才可以正确的将YUV转换到RGB。
如果是系统采集出来给到用户的图像就是YUV的话,你也需要获取这个YUV的存储格式、转换标准和Color Range。这样才能保证正确地处理YUV和RGB之间的转换。
好了,这就是关于RGB和YUV颜色空间的一些知识。在最后我需要再一次强调一下前一节课中讲到的Stride。
在处理YUV图像的存储和读取的时候,也是有Stride这个概念的。事实上,YUV出问题的情况更多。在这里举一个例子,比如说一张1283x720的图像,一个Y分量存储按16字节对齐的话应该是每行占用1296个字节,所以每读取一行像素的Y应该是1296个字节,具体如下图所示。千万不要认为是1283个字节,不然就会出现“花屏”。这里一定要注意。
今天的知识点到这里就讲完了。总结一下,这节课我们主要讲了图像的颜色空间。图像的颜色空间主要有RGB和YUV两种。其中RGB图像每一个像素有R、G、B三个值。而YUV图像有3种类型,其分类如下:
同时,YUV在存储的时候也有两种大类:一种是Planar格式;一种是Packed格式。其中Planar格式又分为先存U还是先存V两种。而Packed格式是UV交错存储且分为U在前还是V在前两种。
在图像采集的时候我们一般得到的原始图像是RGB图像,并且渲染的时候最终也是用RGB图像,而在编/解码时用的却是YUV图像。因此,我们需要在RGB和YUV之间互转。转换的标准有两种:一种是BT601;一种是BT709。
另外,在转换过程中我们还涉及到Color Range这个概念。Full Range的R、G、B三个值的范围都是0~255。而Limited Range的取值范围是16~235。在做转换的时候我们需要选择正确的标准和Color Range。
最后,我再一次提出了Stride这个概念。在读取YUV图像一行像素的时候一定要区分Width和Stride的区别。Width是原始图像的宽,而Stride是对齐之后的一行像素使用的字节大小。这个一定要注意,千万不要弄错了。
通过这节课的学习,现在你知道我们为什么编码的时候使用YUV图像而不使用RGB图像了吗?
好了,这节课到这里就结束了,欢迎留言和我分享你的思考和疑惑,你也可以把今天所学分享给身边的朋友,邀请他加入探讨,共同进步。