图像处理基础
一、常见概念
1、像素
像素是图像的基本单元,一个个像素就组成了图像。可以认为像素就是图像中的一个点。在下面这张图中,可以看到一个个方块组成图像,可以认为这一个个方块就是像素。可以理解成:像素就是一个带有颜色的小块。

2、分辨率
图像(或视频)的分辨率是指图像的大小或尺寸。一般用像素个数来表示图像的尺寸。比如说一张 1920x1080 的图像,前者 1920 指的是该图像的宽度方向上有 1920 个像素点,而后者 1080 指的是图像的高度方向上有 1080 个像素点。
视频行业常见的分辨率有 QCIF(176x144)、CIF(352x288)、D1(704x576 或 720x576),还有我们比较熟悉的 360P(640x360)、720P(1280x720)、1080P(1920x1080)、4K(3840x2160)、8K(7680x4320)等。
随着图像的分辨率越来越高,图像的细节就越来越清晰。
——但是这里的说法有一定问题,如果是原始图像,那确实是分辨率越高越清晰。但是我们看到的图像往往是后期处理过的,例如放大缩小、磨皮美颜。经过处理后的图像(尤其是放大后的图像,分辨率很高,但是并没有很清晰)。

这里添加的原始图像的要求,其实也就是限制了这里的像素表达的就是信息量的意思。
清晰度真正取决于:

3、位深
彩色rgb图像中的像素是有三个颜色值的,分别是红、绿、蓝三个值。也就是说我们看到的那个带有颜色的块其实是由 R、G、B 三个值组成的(有的时候还会有 Alpha 值,代表透明度)。
通常 R、G、B 各占 8 个位,也就是一个字节。8 个位能表示 256 种颜色值,那 3 个通道的话就是 256 的 3 次方个颜色值,总共是 1677 万种颜色。我们称这种图像是 8bit 图像,而这个 8bit 就是位深。
位深越大,我们能够表示的颜色值就越多。因此,图像就可以更精确地展示你拍摄的真实世界。比如现在有 10bit 图像和 12bit 图像,8bit 图像的每一个像素需要占用 3x8 总共 24 个位,3 个字节,同理 10bit、12bit 就会占用更多。
所以,图像的位深越大,需要的存储空间就会越大,传输这张图像使用的流量就会越多。目前我们大多数情况下看到的图像以及视频还是 8bit 位深的。
4、Stride
Stride 也可以称之为跨距,是图像存储的时候有的一个概念。它指的是图像存储时内存中每行像素所占用的空间。一张图像的分辨率确定了,那一行的像素值不就确定了吗?为什么还需要跨距这个东西呢?其实,为了能够快速读取一行像素,我们一般会对内存中的图像实现内存对齐,比如 16 字节对齐。
举个例子,我们现在有一张 RGB 图像,分辨率是 1278x720。我们将它存储在内存当中,一行像素需要 1278x3=3834 个字节,3834 除以 16 无法整除。因此,没有 16 字节对齐。所以如果需要对齐的话,我们需要在 3834 个字节后面填充 6 个字节,也就是 3840 个字节做 16 字节对齐,这样这幅图像的 Stride 就是 3840 了。如下图所示:

这里在处理数据的时候,就要:每读取一行数据的时候需要跳过这多余的 6 个字节。如果没有跳过的话,这 6 个字节的像素就会被我们误认为是下一行开始的 2 个像素(每个像素 R、G、B 各占 1 个字节,2 个像素共 6 个字节)。那这样得到的图像就完全错了,显示出来的就是“花屏”现象,屏幕会出现一条条的斜线。
很多时候,尤其是不规则分辨率的时候,它和图像的 Width(R、G、B 的话就是 Width x 3)是不一样的。有的时候即便图像的 Width 是一个规则的值,比如说 1920 或者 1280 等能被 16 整除的宽度,图像存储在内存中有可能 Stride 和 Width(R、G、B 的话就是 Width x 3)也是不一样的,尤其是不同的视频解码器内部实现的不同,会导致输出的图像的 Stride 不一样。
5、帧率
视频是由一系列图像组成的,即“连续”的一帧帧图像就可以组成视频。但事实上,视频中的图像并不是真正意义上的连续。也就是说,在 1 秒钟之内,图像的数量是有限的。只是当数量达到一定值之后,人的眼睛的灵敏度就察觉不出来了,看起来就是连续的视频了。
这个 1 秒钟内图像的数量就是帧率。一般帧率达到 10~12 帧每秒,人眼就会认为是流畅的了。通常,我们在电影院看的电影帧率一般是 24fps(帧每秒),监控行业常用 25fps。
选择帧率的时候还需要考虑设备处理性能的问题,尤其是实时视频通话场景。帧率高,代表着每秒钟处理的图像数量会很高,从而需要的设备性能就比较高。如果是含有多个图像处理过程,比如人脸识别、美颜等算法的时候,就更需要考虑帧率大小和设备性能的问题。同样,也要考虑带宽流量的问题。帧率越大,流量也会越多,对带宽的要求也会越高。
6、码率
视频的帧率越高,1 秒钟内的图像数据量就会越大。通常我们存储视频的时候需要对图像进行压缩之后再存储,否则视频会非常大。
压缩之后的视频我们一般如何描述它的大小呢?一般对于一个视频文件,我们直接看视频的大小就可以了。但是在实时通信或者直播的时候,视频是视频流的形式,我们怎么衡量呢?
——用码率来衡量。码率是指视频在单位时间内的数据量的大小,一般是 1 秒钟内的数据量,其单位一般是 Kb/s 或者 Mb/s。通常,我们用压缩工具压缩同一个原始视频的时候,码率越高,图像的失真就会越小,视频画面就会越清晰。但同时,码率越高,存储时占用的内存空间就会越大,传输时使用的流量就会越多。
同一个原始视频被压缩之后,真的是码率越高,清晰度就越高吗?
准确来说其实不是,因为视频的压缩是一个非常复杂的过程。事实上,视频压缩之后的清晰度还跟压缩时选用的压缩算法,以及压缩时使用的压缩速度有关。压缩算法越先进,压缩率就会越高,码率自然就会越小。压缩速度越慢,压缩的时候压缩算法就会越精细,最后压缩率也会有提高,相同的清晰度码率也会更小。
所以,并不是说码率越高清晰度就越高。
二、RGB和YUV颜色空间
1、RGB
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 三个颜色是有相关性的,所以不太方便做图像压缩编码。
2、YUV
跟 RGB 图像中 R、G、B 三个通道都跟色彩信息相关这种特点不同,YUV 图像将亮度信息 Y 与色彩信息 U、V 分离开来。Y 表示亮度,是图像的总体轮廓,称之为 Y 分量。U、V 表示色度,主要描绘图像的色彩等信息,分别称为 U 分量和 V 分量。这样一张图像如果没有了色度信息 U、V,只剩下亮度 Y,则依旧是一张图像,只不过是一张黑白图像。Y 分量就是黑白图像,而且包含了图像的总体轮廓信息,只是没有色彩信息而已。
(1)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,每一个 Y 对应一组 UV。YUV 4:2:2,每两个 Y 共用一组 UV。YUV 4:2:0,每四个 Y 共用一组 UV。
(2)不同种类的存储方式
YUV 存储方式主要分为两大类:Planar 和 Packed 两种。Planar 格式的 YUV 是先连续存储所有像素点的 Y,然后接着存储所有像素点的 U,之后再存储所有像素点的 V,也可以是先连续存储所有像素点的 Y,然后接着存储所有像素点的 V,之后再存储所有像素点的 U。Packed 格式的 YUV 是先存储完所有像素的 Y,然后 U、V 连续的交错存储。
①YUV 4:4:4

可以看到,YUV 4:4:4 和 RGB 图像存储之后的大小是一样的。如果是 8bit 图像,就是每一个像素点需要占用 3 个字节。
②YUV 4:2:2
这种类型的 YUV 稍微复杂些,每左右两个像素的 Y 共用一个 U 和一个 V。存储方式主要有以下4种类型。
<1>YU16(或者称为 I422、YUV422P):该类型是 Planar 格式,先存储完 Y,再存储 U,之后存储 V。下图表示的是4x2像素的YU16存储图。

<2>YV16(YUV422P):该类型也是 Planar 格式,先存储完 Y,再存储 V,之后存储 U。例如,4 x 2 像素的 YV16 存储图如下图所示:

<3>NV16(YUV422SP):这种类型是 Packed 格式,先存储完 Y,之后 U、V 连续交错存储。例如,4 x 2 像素的 NV16 存储图如下图所示:

<4>NV61(YUV422SP):这种也是 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 4:2:0
这是最常见也是最常用的 YUV 类型。通常视频压缩都是 YUV 4:2:0 格式的。它是每上、下、左、右 4 个像素点共用一个 U 和一个 V。存储方式主要分为以下4种。
<1>YU12(I420、YUV420P):这种类型是 Planar 格式,先存储完 Y,再存储 U,之后存储 V。例如,4 x 4 像素的 YU12 存储图如下图所示:

<2>YV12(YUV420P):该类型也是 Planar 格式,先存储完 Y,再存储 V,之后存储 U。例如,4 x 4 像素的 YV12 存储图如下图所示:

<3>NV12(YUV420SP):这种类型是 Packed 格式,先存储完 Y,之后 U、V 连续交错存储。例如,4 x 4 像素的 NV12 存储图如下图所示:

<4>NV21(YUV420SP):这种也是 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 个字节。
(3)总结

三、RGB和YUV之间的转换
一般来说,采集到的原始图像、给显示器渲染的最终图像都是 RGB 图像,但是视频编码一般用的是 YUV 图像。那么这中间一定少不了两者的相互转换。
1、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。
2、标准规范
目前YUV和RGB互相转换的标准主要是 BT601 和 BT709。简单来讲,BT709 和 BT601 定义了一个 RGB 和 YUV 互转的标准规范。只有我们都按照标准来做事,那么不同厂家生产出来的产品才能对接上。BT601 是标清的标准,而 BT709 是高清的标准。
两大标准规范下的转换公式如下:

从上图我们可以看到每种标准下不同 Color Range 的转换公式是不同的。所以在做 RGB 往 YUV 转换的时候我们需要知道是使用的哪个标准的哪种 Range 做的转换,并告知对方。这样对方使用同样的标准和 Range 才可以正确的将 YUV 转换到 RGB。
如果是系统采集出来给到用户的图像就是 YUV 的话,也需要获取这个 YUV 的存储格式、转换标准和 Color Range。这样才能保证正确地处理 YUV 和 RGB 之间的转换。
3、Stride的注意项
在处理 YUV 图像的存储和读取的时候,也是有 Stride 这个概念的。事实上,YUV 出问题的情况更多。在这里举一个例子,比如说一张 1283x720 的图像,一个 Y 分量存储按 16 字节对齐的话应该是每行占用 1296 个字节,所以每读取一行像素的 Y 应该是 1296 个字节,具体如下图所示。千万不要认为是 1283 个字节,不然就会出现“花屏”。这里一定要注意。

