为什么要有帧内预测?因为一般来说,对于一幅图像,相邻的两个像素的亮度和色度值之间经常是比较接近的,也就是颜色是逐渐变化的,不会一下子突变成完全不一样的颜色。而进行视频编码,目的就是利用这个相关性,来进行压缩。

很好理解,存储一个像素的亮度值可能需要8个bit,但是如果相邻的两个像素变化不大,我存储一个像素的原始值,以及第二个像素相对第一个像素的变化值,那么第二个值我可能用2个bit就够了,这就节约了很多的空间。而节约存储消耗的bit数,也就是节约码率,贯穿了H.264编码器的所有过程,不管是帧内预测、帧间预测、变换、量化、熵编码,一切的一切都是为这个目的服务的,明白了这一点,我们就能轻易的理解H.264编码器,所有看上去复杂难懂的地方,都是为了一个目的——节约码率。

说回到帧内预测,帧内预测的流程见下图

首先,是上图中蓝色的部分,假设现在我需要对一个像素X进行编码,在编码这个像素之前,先假设我已经有一个参考像素X'了,这个参考像素与同一帧的临近像素有关,根据参考像素X'的值,我得到了一个预测值Xp。

然后,是上图中红色的部分,我用编码的像素X减去预测值Xp,得到了残差d,这个残差d代替原始值X被编码进最终的图像,起到了节省码率的作用

最后,是上图中黑色的部分,残差d和预测值Xp相加,得到了X',用于下一个像素的预测。

总结起来就是三步:

1、以同一帧图像内的临近像素作为参考,计算预测值Xp

2、原始值X和预测值Xp的差值d,被传递到解码端

3、解码端接收到差值d,将其与预测值Xp相加,就得到了“原始值”X',X'=Xp+d

步骤很简单,但是里面有几个问题要明确,首先要知道的是,我们固然可以按像素来进行预测,但是这样太费事了,要计算很多次,而且由于帧内预测的特性,你要预测当前的像素,能参考的像素只能是它的临近像素,也就是前面已经编码完成的,它后面的像素还没有编到,所以自然是不能用来编码当前像素的,因此我们只能一个像素一个像素进行编码,编完了一个才能编下一个,这样显然会很慢。于是H.264标准中提出按块进行计算,一个宏块是16x16像素,然后它可以分成子块,最小是4x4的(这个大小是对于亮度编码而言,至于色度编码,4:2:0格式的色度宏块的长和宽都是亮度宏块的一半),这样也能大大提高计算速度。因此下面提到的“值”可以代表“像素”也可以代表“块”,从原理上来说是一样的,而实际采用的是“块”,因此我也就统一用块这个词了。

我们从上面三个步骤从头开始一点点找问题

1、这个参考值X'是怎么来的?答案是在第三步里,使用Xp和传递过来的残差d相加得到的,这个X'用于后面的块编码时的参考。

2、预测值Xp是怎么从X'获得的?答案是根据X',通过某个公式计算得到的。而这个X'并不是只有一个块,而是有左侧、左上、正上、右上一共四个块作为参考。

3、上面提到的这个某个公式是什么?根据白皮书,Intra(帧内预测)有两种,一种是4x4大小的亮度块,一种是16x16大小的亮度块。

对于4x4大小的亮度块,我们有9种预测模式,如下图所示

对于预测模式0(vertical),当前块的十六个像素值,完全由其上方块最后一行的那四个像素值决定,第一列所有的Xp值都等于A,第二排都等于B,以此类推。预测模式1(horizontal)也是一样,完全由图中IJKL四个像素值决定。

对于预测模式2(DC),则十六个像素值完全相等,等于ABCDIJKL这八个像素的平均值。

对于预测模式3-8,倾斜方向的,各个像素是由A到L像素通过权重不等的公式加权计算的

比如对于模式3(diagnal down-left)来说,a=(A+2B+C+2)/4,这里+2代表四舍五入,b和e=(B+2C+D)/4,cfi=(C+2D+E+2)/4,dgjm=(D+2E+F+2)/4,hkn=(E+2F+G+2)/4,lo=(F+2G+H+2)/4,p=(G+2H+H+2)/4=(G+3H+2)/4

对于模式4(diag down-right),加权系数也是(1,2,1)/4,afkp用IMA三个像素计算,以此类推

对于模式5(vertical right),aj=(M+A+1)/2,同理bk是AB均值,cl是BC均值,d是CD均值(因为这几个像素延长线都不在预测用的13个像素里面)。en在M的延长线上,所以等于(I+2M+A+2)/4,同理fo\gp\h\i\m都可以计算出来

同理模式6、7、8也基本跟5一样,注意对于模式8来说,klmnop四个像素都等于L,因为其延长线在L的下面和前面,没有可以平均的像素,于是只好用L一个值代替了。

还有一点要注意的是,这里面A到L的像素有的没有怎么办?对于模式2(DC),有什么用什么,反正是求均值就对了。其余的模式就必须用到的那几个像素都存在才行(EFGH四个像素可以不存在,此时认为都等于D)

最终,我们要选择哪种模式进行预测?为了在后面节约码率考虑,当然是预测的越准越好,也就是选择Xp和X'差距最小为好。而评判这个差距其实也有好几种算法,比如SAD、SATD等,等到了变换那里再详细说。总之我们用一个公式把上面9种模式的预测都评价了一番,选出里面最好的一种,作为4x4帧内预测的选择。

我们注意到这里有9种模式,之后要进行编码的话,我们除了把残差编进去,总得知道我预测的时候用了哪种模式吧,9这个数就尴尬了,因为刚好三个比特可以表示8种,四个比特可以表示16种,所以3bit不够4bit又浪费了。怎么办呢?有个方法就比较巧妙,我有1bit用来表示我当前用的模式和前面的是不是一样的,因为经常有这样的情况,我前面块用的预测方向和现在这个块用的预测方向一样(比如物体边缘是一条直线,那么对应的那几个块用的预测方向很可能都是一样的),如果一样,我只用1bit就足够存储了,如果不一样,我再用用4个bit存储,也就达到了节约bit的目的。

说完了4x4的亮度块,我们看看16x16大小的亮度块。16x16亮度块有四种模式,如下图

前面三种很好理解了,重点是第四种plane的计算方式,从图上看大概能理解,不过具体算法我还不是很理解。

我们假设左上角起,上方那一行是17个像素是a1 b2 c3 d4 e5 f6 g7 h8 i9 j8 k7 l6 m5 n4 o3 p2 q1,用这17个像素计算一个H值。

我在上面标了1~9~1的数字,有数字相同的8对像素,后面计算的时候,都是一对对的计算的。

i9;j8 - h8;k7 - g7;l6 - f6;m5 - e5;n4 - d4;o3 - c3;p2 - b2;q1 - a1,这九对分别乘以权重0到8(也就是i9这个像素没有用到),而最左边和最右边两个像素权重最大。

同理V值,也是一样的算法,从上到下像素记作带'的(当然a1' = a1)。

然后计算一个A值,它等于右上角(q1)和左下角(V值计算时候对应的那个q1')的和乘以16

计算一个B值,它等于(5*H+32)/64,计算一个C值,C=(5*V+32)/64,这种后面加了32又除以64的,其实都是用来四舍五入的。

然后就能计算我们的预测值了。

i00 = A - 7*B -7*C + 16

pix[0][0] = i00 / 32(超出0到255范围的要截断成0或者255)

然后计算第一行十六个像素分别是i00 + B到i00 + 15*B,然后除以32(然后截断到0~255)

第二行是在第一行基础上加了一个C,一直到第16行,加了15个C,于是这256个像素都算出来了。

这就是plane方式的算法。算出来结果就跟下图一样。

  

对于色度块是亮度块的四分之一,也就是8x8的,那就只有一种了,预测模式也跟亮度16x16块的类似,有四种,只不过具体的序号不一样而已。是0代表DC,1代表horizontal,2代表vertical,3代表plane。

plane的算法和上面16x16的类似,只不过系数变了,而且两个色度块用的方式一定都是一样的。

好了,我们回到前面的帧内预测的步骤,还有几个问题没有解决

4、重建值X'和原始值是不是一样的?答案是不一样,因为d在传到解码端的过程中经过了量化、变换、反变换和反量化,有了精度的损失,因此Xp+d得到的X'跟原始的X是不一样的。这也是为什么要用重建值X'来得到Xp而不是用原始值X。因为解码器那边在解码图像的时候,用到的是有损的X'获得Xp,如果编码器用无损的X来得到Xp的话,那么得到的残差d在编码侧和解码侧就不一致了,这个误差扩散到了下一个块里。

好了,这样上面的步骤就组成了一个循环,可以一直编码下去了,当然最开始没有参考的那个块,其预测值Xp又是怎么来的呢?可以有一种方式叫做DC128,也就是认为这个块的每个像素都是0x80,然后依据此来计算。

实际上对于一个16x16的宏块,上面这个16x16的四种模式和它16个子块(大小4x4)的9种模式都会算一遍,然后用16个子块的SATD最小值和16x16的四种模式的SATD最小值比较,选择更小的那个,所以这里面计算量还是很大的。x264源代码里有很多节约计算量的设计,不过这些就得看代码了,白皮书里面是没有的,以后再做研究。

另外x264里面还有Intra8x8亮度块的模式,这种我认为应该也是为了找到一个预测最准的模式而加进去的,而且也不常用,所以也就不多介绍了。

回过头来看最开始的那个步骤图,跟白皮书里给的编码器流程图,是不是正是编码器的一部分呢?

H.264 White Paper学习笔记(二)帧内预测的更多相关文章

  1. H.264 White Paper学习笔记(一)总览

    H.264 White Paper对于264编码器的原理讲的比较透彻,在阅读学习的时候收获很大,这份文献网上有很多了,也有不少人翻译,不过想要理解更清楚我觉得还是得看英文原版的. 首先看一下白皮书里给 ...

  2. H.264学习笔记2——帧内预测

    帧内预测:根据经过反量化和反变换(没有进行去块效应)之后的同一条带内的块进行预测. A.4x4亮度块预测: 用到的像素和预测方向如图: a~f是4x4块中要预测的像素值,A~Q是临块中解码后的参考值. ...

  3. H.264学习笔记3——帧间预测

    帧间预测主要包括运动估计(运动搜索方法.运动估计准则.亚像素插值和运动矢量估计)和运动补偿. 对于H.264,是对16x16的亮度块和8x8的色度块进行帧间预测编码. A.树状结构分块 H.264的宏 ...

  4. H264提供了哪些帧内预测?

    H.264/AVC 提供了四种帧内预测方式:4x4 亮度块的帧内预测(Intra_4x4).16x16 亮度块的帧内预测(Intra_16x16).8x8 色度块的帧内预测(Intra_chroma) ...

  5. NumPy学习笔记 二

    NumPy学习笔记 二 <NumPy学习笔记>系列将记录学习NumPy过程中的动手笔记,前期的参考书是<Python数据分析基础教程 NumPy学习指南>第二版.<数学分 ...

  6. Django学习笔记二

    Django学习笔记二 模型类,字段,选项,查询,关联,聚合函数,管理器, 一 字段属性和选项 1.1 模型类属性命名限制 1)不能是python的保留关键字. 2)不允许使用连续的下划线,这是由dj ...

  7. 学习笔记(二)--->《Java 8编程官方参考教程(第9版).pdf》:第七章到九章学习笔记

    注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法者自负一切 ...

  8. Linux内核学习笔记二——进程

    Linux内核学习笔记二——进程   一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器 ...

  9. muduo学习笔记(二)Reactor关键结构

    目录 muduo学习笔记(二)Reactor关键结构 Reactor简述 什么是Reactor Reactor模型的优缺点 poll简述 poll使用样例 muduo Reactor关键结构 Chan ...

随机推荐

  1. Spring cloud consul 相关前提知识

    Spring boot .vs.  Spring mvc  spring boot extends spring mvc extends spring Spring Boot uses Spring ...

  2. linux网络配置练习

    查看网卡是否正常安装 命令:lspci |grep Ether 1.修改网卡配置 命令: vi /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth ...

  3. dubbox 的各种管理和监管[转]

    dubbo官方自带了dubbo-admin及dubbo-simple/dubbo-monitor-simple二个子项目用于服务治理及服务监控. 一.dubbo-admin的部署 这个比较简单,编译打 ...

  4. 系统目录结构/ls命令/文件类型/alias命令

    2.1/2.2 系统目录结构 2.3 ls命令 2.4 文件类型 2.5 alias命令 linux文件目录结构 linux文件结构 / 系统跟目录 root  root用户主目录,存放启动linux ...

  5. 搭建交叉调试环境 arm-linux-gdb配合gdbserver

        在嵌入式开发中,有时候需要进行源码级别的调试,可以设置断点,单步执行,相比于每步打印printf或者printk来说,更加友好.下面就来介绍这种调试方法.     gdb交叉调试类似于网络浏览 ...

  6. LR进行接口测试

    其实无论用那种测试方法,接口测试的原理是通过测试程序模拟客户端向服务器发送请求报文,服务器接收请求报文后对相应的报文做出处理然后再把应答报文发送给客户端,客户端接收应答报文这一个过程. 方法一.用Lo ...

  7. ADCD 1.9 ZOS 配置 CTCI-W32 TCPIP 网络

    试验步骤:两步走,第一步修改Hercules的配置文件 在hercules 配置文件末尾加上    0E20-0E21 CTCI     -n 0A-00-27-00-00-00  192.168.5 ...

  8. VC++调用MSFlexGrid的SetRow方法,出现异常“Invalid Row Value”

    MSFlexGrid是微软提供的网格表格控件,SetRow方法用于设置当前焦点所在行.  C++ Code  12345   void CMSFlexGrid::SetRow(long nNewVal ...

  9. js将json数据以csv格式下载

    摘要: 最近有一个非项目的小需求,就是将项目开发分工文件化,方便后期管理维护.但是开发时,分工安排都是以json格式记录的,所以就做了一个将json数据以csv格式下载到本地. 代码: <!DO ...

  10. 输入控件tagsinput

    ​摘要: ​tagsinput是一款基于jQuery的插件.具有组织输入内容.校验.backspace删除等功能.当你在输入框输入结束按下enter键,tagsinput会将你输入的内容用标签封装,每 ...