You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
14 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 08变换量化如何减少视觉冗余
你好,我是李江。
在前面几节课里面我们对[视频编码的原理](https://time.geekbang.org/column/article/459554)、[帧内编码](https://time.geekbang.org/column/article/462268)和[帧间编码](https://time.geekbang.org/column/article/463775)都做了详细的介绍。我们知道通过帧内编码可以去除空间冗余通过帧间编码可以去除时间冗余而为了分离图像块的高频和低频信息从而去除视觉冗余我们需要做DCT变换和量化。
为了让你更透彻地了解视频编码中的DCT变换和量化的原理在这节课里面我们会对DCT变换和量化的过程做一个深入的探讨。通过下图你可以很清楚地看到视频编码的过程并且能够更直观地感受DCT变换和量化在整个视频编码过程中的重要性。
![](https://static001.geekbang.org/resource/image/06/78/064a2356cc7bbf4c1c43d2856e186f78.jpg?wh=1280x572)
由于DCT变换和量化过程是一个跟数学比较相关的过程且大多数是数学计算。因此今天的课程中数学公式和计算过程相对会多一些。但是总体来说难度不是很大。
下面我们就先来讨论一下常规的视频编码中的DCT变换和量化看看它是怎么去除一部分高频信息来达到去除视觉冗余的。并且由于H264中用到的DCT变换和量化跟常规的DCT变换和量化有一些区别其主要在于H264使用整数变换代替常规的DCT变换并将DCT变换中的一部分计算整合到量化中从而减少浮点运算漂移问题。因此我们还会对H264中的DCT变换和量化做一下介绍最后对比一下H264中的变换和量化与常规的变换和量化的区别。
## 常规视频编码中的DCT变换和量化
通过前面的学习我们知道DCT变换和量化的目的是去除视觉冗余。接下来让我们看一下常规视频编码中DCT变换和量化是如何实现这一目的的。
### DCT变换
**DCT变换就是离散余弦变换。它能够将空域的信号对于图像来说空域就是你平时看到的图像转换到频域对于图像来说就是将图像做完DCT变换之后的数据上表示并能够比较好的去除相关性。**其主要用于视频压缩领域。现在常用的视频压缩算法中基本上都有DCT变换。
在视频编码原理那节课中我们也讲过图片经过DCT变换之后低频信息集中在左上角而高频信息则分散在其它的位置。通常情况下图片的高频信息多但是幅值比较小。高频信息主要描述图片的边缘信息。
由于人眼的视觉敏感度是有限的有的时候我们去除了一部分高频信息之后人眼看上去感觉区别并不大。因此我们可以先将图片DCT变换到频域然后再去除一些高频信息。这样我们就可以减少信息量从而达到压缩的目的。
**DCT变换本身是无损的同时也是可逆的。**我们可以通过DCT变换将图片从空域转换到频域也可以通过DCT反变换将图片从频域转回到空域。下面我们来看一下DCT变换的公式。
一维DCT变换公式如下其中f(i)是指第i个样点的信号值N代表信号样点的总个数。
![](https://static001.geekbang.org/resource/image/b7/34/b767c40657b6758aff71fb148ce4da34.jpg?wh=1280x474)
二维DCT变换公式如下其中f(i, j)是指第(i, j)位置的样点的信号值N代表信号样点的总个数。
![](https://static001.geekbang.org/resource/image/d3/bd/d310a208e95a8d439bb806c6aa10ddbd.jpg?wh=1280x484)
一般在编码标准中图像是进行二维DCT变换的因为图像是个二维信号。但是实际上在代码里面我们经常将二维DCT变换转换成两个一维DCT变换来进行。
在视频压缩中DCT变换是在帧内预测和帧间预测之后进行的。也就是说DCT变换其实是对残差块做的。我们在编码时会将图像划分成一个个宏块而宏块又可以划分成一个个子块。那DCT变换是在宏块上进行还是在子块上进行呢
其实,**通常情况下DCT变换是在4x4的子块上进行的**也可以在8x8子块上进行但是只有在扩展profile才支持由于原理是一样的因此这里不再展开讨论即便预测时并没有对宏块再做划分。也就是说不管宏块有没有被划分到4x4的子块我们在做DCT变换时都是在一个个4x4块上进行的。如下图所示
![](https://static001.geekbang.org/resource/image/00/c4/001a5f7eb9e9a08960c1cc3b8841f8c4.jpg?wh=1280x720)
好了如果我们将上面的DCT变换公式用在4x4的变换块上则4x4的DCT变换就可以通过下面的4x4的矩阵乘法来表示了。
![](https://static001.geekbang.org/resource/image/59/5b/593a45c8e6a4dc69a1038efb7fb6bc5b.jpg?wh=1280x720)
为了让你更好地理解DCT变换我们通过下面的例子来看一下4x4的残差块的DCT变换结果。我们称左上角的系数为DC系数而其它系数为AC系数。
![](https://static001.geekbang.org/resource/image/94/3b/94ec636ddc0fa111a51f7149bc7cc43b.jpg?wh=1280x720)
对这个4x4的残差块运用公式可得
![](https://static001.geekbang.org/resource/image/72/eb/7266275ea219bb6dba2e2866bdbeddeb.jpg?wh=1280x640)
我们可以得到下面的图:
![](https://static001.geekbang.org/resource/image/e5/c0/e50bf2884c1fc7ec4a753d61f4b8eac0.jpg?wh=1280x492)
从上面DCT变换的公式和矩阵表示方式中我们可以看到DCT变换的计算过程中涉及到了cos函数。那也就是说计算的过程中一定涉及到了浮点运算。而浮点运算计算速度比较慢。**那有没有什么运算可以将图像块比较快速的转换到频域呢?**答案肯定是有的。前面我们其实已经讲到过那就是Hadamard变换也叫哈达玛变换。
### Hadamard变换
在视频编码过程中Hadamard变换也经常会用到。前面我们在[帧内预测](https://time.geekbang.org/column/article/462268)的[率失真优化](https://time.geekbang.org/column/article/462268)的模式选择里就讲到过Hadamard变换可以代替DCT变换将残差块快速转换到频域以便用来估计一下当前块编码之后的大小。
其实在H264的亮度16x16帧内预测块和色度8x8预测块中也会使用到Hadamard变换。稍后我们会在H264中的DCT变换和量化部分对它进行简单介绍。下面我们先来看一下Hadamard变换的矩阵表示形式。
![](https://static001.geekbang.org/resource/image/37/6c/37ca4e953244ea275e95f5eb03320b6c.jpg?wh=1280x720)
你是不是可以看到Hadamard变换是没有浮点运算的因此其计算速度很快并且也能够将图像块从空域变换到频域。因此我们可以用它一定程度上粗略的代替DCT变换从而用来简化运算。
好了现在我们知道了如何通过DCT变换和Hadamard变换将残差块从空域转换到频域。接下来我们看看在常规视频编码中量化是如何做到通过去除一部分高频信息来最终达到去除视觉冗余的。
### 量化
前面我们讲了我们将图像块变换到频域之后AC系数比较多但是一般幅值比较小。并且我们可以去除一些AC系数达到压缩图像的目的同时人眼看起来差距不大。**这个去除AC系数的操作是什么呢**很明显就是量化了。
其实**量化的操作并不是针对AC系数去做的DC系数也同样会做量化**只是通常情况下DC系数比较大从而量化后变换为0的概率比AC系数要小。量化操作其实非常简单就是除法操作。计算公式如下
![](https://static001.geekbang.org/resource/image/7f/5a/7f020c6yy18d49a13fda06937888cd5a.jpg?wh=1280x358)
在量化过程中最重要的就是QStep用户一般接触到的是QP两者可以查表转换
其中在H264中QP和QStep之间的转换表格如下
![](https://static001.geekbang.org/resource/image/43/ea/4360178571e94yy6d1c916f02282a6ea.jpg?wh=1280x720)
通常QStep值越大DC系数和AC系数被量化成0的概率也就越大从而压缩程度就越大但是丢失的信息也就越多。这个值太大了会造成视频出现一个个块状的效应且严重的时候看起来像马赛克一样这个值比较小的话压缩程度也会比较小从而图像失真就会比较小但是压缩之后的码流大小就会比较大。
我们通过一个例子来看一下量化的结果。
![](https://static001.geekbang.org/resource/image/bb/0c/bb8783ca8e1b1bd7177459a344fb410c.jpg?wh=1280x484)
这就是常规的变换和量化的计算过程。实际上H264里面的变换和量化是这样的吗原理上是的但是实际计算过程变了。因为DCT变换过程中涉及到浮点运算在不同机器上解码会因为精度问题产生漂移导致误差。同样量化过程有除法运算大多数时候其结果还是浮点型的数字在不同机器上解码也会有误差。
## H264中的DCT变换和量化
**H264为了减少这种浮点型运算漂移带来的误差将DCT变换改成了整数变换DCT变换中的浮点运算和量化过程合并这样就只有一次浮点运算过程**以此来减少不同机器上浮点运算产生的误差。下面我们来看看H264中的变换和量化。
### H264的整数变换和量化
我们知道常规的DCT变换的矩阵计算方式如下
![](https://static001.geekbang.org/resource/image/5e/43/5e72913dfa211e19a205c39553db3d43.jpg?wh=1280x720)
而在H264中我们通过下面的推导过程将DCT变换一步步修改为整数变换。最后H264中的DCT变换就变成了整数变换。其矩阵的计算方式如下
![](https://static001.geekbang.org/resource/image/ff/7f/ff71a57bb82d76493c2bc992107d307f.jpg?wh=1280x720)
我们将点乘左边的部分取出来就是H264中的整数变换了。公式如下
![](https://static001.geekbang.org/resource/image/8f/19/8ff14e94f06766df6cdba1706780c019.jpg?wh=1280x720)
我们同样使用上面DCT变换的例子来做一下整数变换。其结果如下
![](https://static001.geekbang.org/resource/image/fe/ec/fe4b65c6e4ba73a15ceacda63651ecec.jpg?wh=1280x472)
在前面整数变换里DCT变换中的点乘部分被拿出来了这一部分的计算被合并到了H264的量化过程中。因此H264的量化过程如下所示
![](https://static001.geekbang.org/resource/image/3b/f1/3b00268b7f298846b60795b534908ff1.jpg?wh=1280x720)
其中MF我们一般都是通过表格查询得到。表格如下。其中对于QP大于5的情况使用QP = QP % 6进行查询。
![](https://static001.geekbang.org/resource/image/a3/1e/a37cdd4f5cb2492e658680f950be2b1e.jpg?wh=1280x720)
同样上面整数变换之后我们用H264的量化公式对其进行量化后得到的结果如下
![](https://static001.geekbang.org/resource/image/cd/7d/cdc4482b0689e8ef6142657a0da3c67d.jpg?wh=1280x468)
我们可以看到虽然H264的DCT变换和量化过程跟常规的DCT变换和量化不一样但是最后量化的结果其实还是一样的。这也是符合预期的。毕竟它们的“目的地”还是一样的只是“走的路”稍微有些不同而已。
好了这就是H264中的DCT变换和量化的基本原理。接下来我们来看看H264各模式块的DCT变换和量化过程具体是怎么样的。
### H264各模式块的DCT变换和量化过程
1. 亮度16x16帧内预测块
亮度16x16块首先被划分成16个4x4的小块做整数变换。变换之后将16个4x4小块的DC系数都拿出来组成一个4x4的DC块再对这个4x4的DC块进行Hadamard变换。然后再总体进行量化操作。
![](https://static001.geekbang.org/resource/image/e1/e6/e10613350835c3ebb14e001461e7eae6.jpg?wh=1280x346)
2. 其它模式亮度块
对于除亮度16x16帧内预测块之外的其它亮度块都是直接划分成4x4的块进行整数变换之后再进行量化操作就可以了。
![](https://static001.geekbang.org/resource/image/c7/cd/c7e61f929cdde6d8624ae0be270126cd.jpg?wh=1280x314)
3\. 色度块
对于YUV420图像色度块大小是8x8。我们先将8x8色度块划分成4个4x4的小块做整数变换。变换之后将4个小块的DC系数拿出来组成一个2x2的DC块再对这个2x2的DC块进行Hadamard变换。最后总体进行量化操作。
![](https://static001.geekbang.org/resource/image/61/16/61c58d94ac755c2c71e8538d772d2b16.jpg?wh=1280x368)
## 小结
我们今天一开始主要讲解了DCT变换的基本原理。DCT变换主要是将图像从空域转换到频域并将图像的高频和低频信息分离开来。虽然高频信息数据多但是幅值比较小。这样高频信息在量化的过程中能够比较容易被减少。这样可以比较有效地减少图像的视觉冗余从而达到压缩的目的。
接着我们简单地介绍了一下量化的原理。量化其实就是一个除法操作。通过除法操作就可以将幅值变小而高频信息幅值比较小就比较容易被量化成0这样就能够达到压缩的目的。
在讲变换的原理的时候我们还讲到了一个前面提到了好几次的Hadamard变换。Hadamard变换在H264的16x16帧内亮度块和8x8色度块中会被用到。但是Hadamard在率失真优化做模式选择的时候使用的更多。基本上各种视频编码都或多或少会用到它来做率失真优化。
在H264标准中我们不会直接使用标准的DCT变换和量化。为了减少多次浮点型运算在解码端产生漂移的问题H264使用整数变换代替DCT变换。DCT变换中的浮点运算部分跟量化过程进行合并将两次浮点型运算变成一次从而减少误差。
在最后我们简单介绍了H264标准中不同模式亮度块和色度块的DCT变换和量化的过程。其中需要注意的就是亮度16x16帧内预测块和色度8x8的DC系数会单独拿出来组成一个新的DC块我们会先对这个DC块进行Hadamard变换之后再做量化操作。
## 思考题
为什么我们在率失真优化的过程中会用Hadamard变换之后的块做大小预估
你可以把你的答案和疑惑写下来,分享到留言区,与我一起讨论。下节课再见。