# 06|帧内预测:如何减少空间冗余? 你好,我是李江。 前面几节课我们一起讨论了视频编码的原理以及编码码流结构,从今天开始我们一起来聊一聊具体的编码模式。 我们知道在视频编码时主要需要减少4个冗余,包括:空间冗余、时间冗余、视觉冗余和信息熵冗余。其中空间冗余就是通过帧内预测的方式来实现的。那帧内预测到底是怎么做到减少空间冗余的呢? 其实一般来说,**一幅图像中相邻像素的亮度和色度信息是比较接近的,****并且****亮度和色度信息也是逐渐变化的,不太会出现突变**。也就是说,**图像具有空间相关性**。帧内预测就是利用这个特点来进行的。即**帧内预测通过利用已经编码的相邻像素的值来预测待编码的像素值,最后达到减少空间冗余的目的**。 这里需要注意的一个点就是,我们是通过已经编码了的像素值去预测待编码的像素值。你可能会问,已经编码了的像素值变成码流了,不再是一个个像素了,怎么去预测待编码的像素呢?其实已经编码了的像素是会重建成重建像素,用来做之后待编码块的参考像素的。你可以认为是**已经编码的块会解码成像素用来做参考像素**。 好了,我们已经知道了帧内预测是怎么减少空间冗余的,那我们还有一个最重要的问题要解答。既然帧内预测是通过相邻像素来预测待编码像素的,那它到底是怎么预测的呢?下面我们就以H264标准来讲解每种帧内预测模式是怎么做预测的。 ## 不同块大小的帧内预测模式 我们在视频编码原理的那节课里面讲过,视频编码是以块为单位进行的。**在H264标准里面,块分为宏块和子块。宏块的大小是16 x 16(YUV 4:2:0图像亮度块为16 x 16,色度****块****为8 x 8)**。在帧内预测中,亮度宏块可以继续划分成16个4 x 4的子块。因为图像中有的地方细节很多,我们需要划分成更小的块来做预测会更精细,所以会将宏块再划分成4 x 4的子块。如下图所示: ![](https://static001.geekbang.org/resource/image/5c/5b/5c0a24c3d52e8yy2de20fb07b58c625b.jpg?wh=1280x446) 帧内预测是根据块的大小分为不同的预测模式的。还有一个点就是亮度块和色度块的预测是分开进行的。这里我给你总结一下,主要有以下3点规则: 1. **宏块大小是16 x 16,其中亮度块为16 x 16,色度块为8 x 8**; 2. **帧内预测中亮度块和色度块是分开独立进行预测的**,即亮度块参考已编码亮度块的像素,而色度块参考已编码色度块的像素; 3. **16 x 16的亮度块可以继续划分成16个4 x 4的子块**。 所以,我们在实际帧内预测的时候就会分为:4 x 4亮度块的预测、16 x 16亮度块的预测、8 x 8色度块的预测(注意亮度8 x 8模式和I\_PCM模式很少使用,我们这里不做讨论)。下面我们就开始一个个展开来讨论吧。 ### 4 x 4亮度块的帧内预测模式 因为4 x 4的块帧内预测模式最多,并且基本包含亮度16 x 16和色度8 x 8的模式,所以我们接下来先从4 x 4亮度块的帧内预测模式讲起。 4 x 4亮度块的帧内预测模式总共有9个。其中有**8种方向模式和一种DC模式**,且方向模式指的是预测是有方向角度的。好了,那我们接下来就聊聊这9种模式吧。 1. Vertical模式 **Vertical模式就是指,当前编码亮度块的每一列的像素值,都是复制上边已经编码块的最下面那一行的对应位置的像素值。**这句有点拗口,别急,等会儿我会给出计算方法和示意图,让你能很容易地理解它。 **Vertical模式得到的预测块同一列中的像素值都是一样的**。该模式得到的块就叫做Vertical预测块。注意,**该模式只有在上边块存在的时候才可用,如果不存在则该模式不可用**。比如图像最上边的块就没有可参考的块存在。 该模式下预测块像素计算方法如下: ![](https://static001.geekbang.org/resource/image/03/e7/03b05d50574c17ed523f9164929019e7.jpg?wh=1280x362) 示意图如下: ![图片](https://static001.geekbang.org/resource/image/c4/d8/c4yy0c94ba713d778fd81917238863d8.png?wh=1160x762) 2. Horizontal模式 **Horizontal模式就是指,当前编码亮度块的每一行的像素值,都是复制左边已经编码块的最右边那一列的对应位置的像素值**(同样地,下面有计算方法和示意图)。**Horizontal模式得到的预测块同一行的像素值都是一样的**,该模式得到的块就叫做Horizontal预测块。注意,**该模式只有在左边块存在的时候才可用,如果不存在则该模式不可用**。比如图像最左边的块就没有可参考的块存在。 该模式下预测块像素计算方法如下: ![](https://static001.geekbang.org/resource/image/81/91/8185bc4a93669c2a800f5cf0743f7091.jpg?wh=1280x342) 示意图如下: ![图片](https://static001.geekbang.org/resource/image/a1/f5/a15b484807812294b00f1aebcbc942f5.png?wh=1154x750) 3. DC模式 **DC模式就是指,当前编码亮度块的每一个像素值,是上边已经编码块的最下面那一行和左边已编码块右边最后一列的所有像素值的平均值**。注意,**DC模式预测得到的块中每一个像素值都是一样的**。DC模式得到的块就叫做DC预测块。 根据上边块和左边块是不是存在,该模式下预测块像素的计算方法分为以下四种情况: ![](https://static001.geekbang.org/resource/image/85/ff/85de3eafb25bff062e821f83f11236ff.jpg?wh=1280x720) 示意图如下: ![图片](https://static001.geekbang.org/resource/image/0y/6b/0yyb8202b3dd85233975ea3cffc13b6b.png?wh=1160x760) 4. Diagonal Down-Left模式 **Diagonal Down-Left模式是上边块和右上块**(上边块和右上块有可能是一个块,因为可能是一个16 x 16的亮度块,意思理解就可以)**的像素通过插值得到**。**如果上边块和右上块不存在则该模式无效。** 该模式下预测块计算的方法分为下面两种情况: ![](https://static001.geekbang.org/resource/image/a6/06/a6b8b26163ca9d9274377b5735785f06.jpg?wh=1280x412) 示意图如下: ![图片](https://static001.geekbang.org/resource/image/11/a1/110b13f5d5af1d119b6f2a559e39f7a1.png?wh=1178x754) 5. Diagonal Down-Right模式 **Diagonal Down-Right模式需要通过上边块、左边块和左上角对角的像素通过插值得到。如果这三个有一个不存在则该模式无效**。 该模式下预测块计算的方法有以下三种情况: ![](https://static001.geekbang.org/resource/image/4f/b7/4f10404d6fc9e3625092d92090aed6b7.jpg?wh=1280x474) 示意图如下: ![图片](https://static001.geekbang.org/resource/image/fc/93/fcbdf7eaec95fea21158b4ce1047ed93.png?wh=1154x760) 6. Vertical-Right模式 **Vertical-Right模式是需要通过上边块、左边块以及左上角对角的像素插值得到的**。必须要这三个都有效才能使用,否则该模式无效。 该模式下预测块计算的方法有以下四种情况: ![](https://static001.geekbang.org/resource/image/63/ce/63b391728df65ce7551012753ae331ce.jpg?wh=1280x720) 示意图如下: ![图片](https://static001.geekbang.org/resource/image/c5/bf/c5d488c6a80b9c27ba50048fb4ef4cbf.png?wh=1150x732) 7. Horizontal-Down模式 **Horizontal-Down模式需要通过上边块、左边块以及左上角对角的像素插值得到。必须要这三个都有效才能使用,否则该模式无效。** 该模式下预测块计算的方法有以下四种情况: ![](https://static001.geekbang.org/resource/image/12/40/120761b8313c18c7433f7708d8b4a140.jpg?wh=1280x720) 示意图如下: ![图片](https://static001.geekbang.org/resource/image/6a/bc/6a3ab22c2570589ac2680c561be0c5bc.png?wh=1172x756) 8. Vertical-Left模式 **Vertical-Left模式是需要通过上边块和右上块**(上边块和右上块有可能是一个块,因为可能是一个16 x 16的亮度块,意思理解就可以)**最下面一行的像素通过插值得到**。如果这两种块不存在则该模式不可用。该模式下预测块的计算方法有下面两种情况: ![](https://static001.geekbang.org/resource/image/83/66/835d3ec11768f9928098ab2e54556466.jpg?wh=1280x442) 示意图如下: ![图片](https://static001.geekbang.org/resource/image/5e/5d/5e13e4207abd6788b189d9e86669665d.png?wh=1170x774) 9. Horizontal-Up模式 **Horizontal-Up模式是需要通过左边块的像素通过插值得到的**。如果左边块不存在,则该模式不可用。 该模式下预测块的计算方法有下面四种情况: ![](https://static001.geekbang.org/resource/image/7b/59/7bbb469c4f050a098b0744ea3cc94e59.jpg?wh=1280x720) 示意图如下: ![图片](https://static001.geekbang.org/resource/image/53/f8/53ddf8a40698cb99e48f103206b149f8.png?wh=1156x764) ### 16 x 16亮度块的帧内预测模式 上面我们讲了4 x 4亮度块的9种预测模式,接下来我们来聊一下16 x 16亮度块的预测模式。 **16 x 16亮度块总共有****4****种预测模式。****它们****分别是****Vertical****模式,****Horizontal****模式、DC模式****和****Plane模式**,前面三种模式跟4 x 4的原理是一样的这里就不重复讲解了。16 x 16亮度块的帧内预测的4种模式的示意图如下: ![图片](https://static001.geekbang.org/resource/image/e3/e6/e303d85a11dced7978cedea604f001e6.png?wh=1920x872) ![图片](https://static001.geekbang.org/resource/image/b0/24/b09910c6bc61b97924a52dcd89c04824.png?wh=1920x885) 接下来我们介绍一下Plane模式。这种模式相比前面三种模式稍微复杂一些,但是基本原理都差不多。**Plane预测块的每一个像素值,都是将上边已编码块的最下面那一行,和左边已编码块右边最后一列的像素值经过下面公式计算得到的**。 ![](https://static001.geekbang.org/resource/image/4y/f2/4yyf0416de0dda660b69cb3b59b36ff2.jpeg?wh=2428x1076) ### 8 x 8色度块的帧内预测模式 上面我们讲述了亮度块的帧内预测模式,接下来我们简单介绍一下色度块的帧内预测模式。 8 x 8色度块的帧内预测模式跟16 x 16亮度块的是一样的,也是总共有4种,分别为DC模式、Vertical模式,Horizontal模式、Plane模式。与16 x 16亮度块不同的是,块大小不同,所以参考像素值数量会不同。但是基本是一致的,这里就不重复了。 ## 帧内预测模式的选择 现在我们学习了这么多的模式,而每一个块却只能有一种帧内预测模式。那我们怎么确定一个块到底使用哪种模式呢?我们这边先把思路讲一讲,具体细节不展开。等到你把编码基础弄明白了之后可以阅读一下x264的代码,里面有关于具体如何去选择模式的方法。 以亮度块为例。一个16 x 16的亮度块,我们可以不划分,直接使用4种16 x 16的帧内预测模式,最多得到4种预测块;也可以划分成16个4 x 4的子块,每一个子块最多有9种帧内预测模式。 对于每一个块或者子块,我们可以得到预测块,再用实际待编码的块减去预测块就可以得到残差块。主要有下面3种方案来得到最优预测模式: * 第一种方案,先对每一种预测模式的残差块的像素值求绝对值再求和,称之为cost,然后取其中残差块绝对值之和也就是cost最小的预测模式为最优预测模式。 * 第二种方案,对残差块先进行Hadamard变换(在DCT变换和量化那节课中会介绍),变换到频域之后再求绝对值求和,同样称为cost,然后取cost最小的预测模式为最优预测模式。 * 第三种方案,也可以对残差块直接进行DCT变换量化熵编码,计算得到失真大小和编码后的码流大小,然后通过[率失真优化](https://www.jianshu.com/p/6f8be3709003?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation)(作为课外内容自行学习,这里不展开讨论)的方法来选择最优预测模式。 为了让你更容易理解第三种方案,这里我稍微简单介绍一下率失真优化的思想**。** 我们知道预测之后经过DCT变换再量化会丢失高频信息。一般来说QP越大,丢失的信息越多,失真就越大,但是码流大小也越小;反之,QP越小,丢失的信息越少,但是码流大小就越大。这是一个跷跷板。**我们一般会在失真和码流大小之间平衡,尽量找到在一定码率下,失真最小的模式作为最优的预测模式,这就是率失真优化的思想**。 其实还有很多不同的方案,比如有的为了加速模式选择的过程,率失真计算的时候,只会进行DCT变换和量化,不会进行熵编码。码流大小直接通过QP值估算或者使用预测模式的大小来代替。这些方案都可以,具体看编码器的实现。一般来说,选择过程越精细效果越好,但是速度会越慢。 通过上面讲的这些方法我们找到了每一个4 x 4块的最优模式之后,将这16个4 x 4块的cost加起来,与16 x 16块的最小cost对比,选择cost最小的块划分方式和预测模式作为帧内预测模式。 ## 小结 好了,今天的课到这里就要结束了。我们来回顾一下这节课的主要内容。 我们主要是一起聊了聊帧内预测。我们先详细讲述了有哪几种预测模式,以及各种预测模式的预测块的计算方法。同时强调一点,就是有的模式在参考像素不存在的时候是无效的,也就是不能使用的。这一点需要注意,并不是列举出来的每一个模式都是可以使用的。 为了方便你记忆,下面我把每一种块和子块支持的预测模式用一个表格来总结一下。 ![](https://static001.geekbang.org/resource/image/ee/2d/eea93f2ace612181dfe81e34dd5e532d.jpg?wh=1280x720) 最后,我们还简单介绍了一下预测模式的选择方法,主要有计算残差块绝对值之和、将残差块做Hadamard变换之后再求和、率失真优化等几种方案来得到cost,然后我们取cost最小的模式作为帧内预测模式。 ## 思考题 在H264标准里面,视频的第一帧的第一个块应该怎么选择预测模式呢? 你可以把你的答案留在评论区,和我一起探讨、交流,看看自己的理解是不是正确。下节课再见。