JM(264)基础知识整理
来源:互联网 发布:java程序员常用网站 编辑:程序博客网 时间:2024/06/05 10:33
码率控制
码率控制实际上是一种编码的优化算法,它用于实现对视频流码流大小的控制。那么它控制的目的是什么呢?
我们可以试想一下,同样的视频编码格式,码流大,它包含的信息也就越多,那么对应的图像也就越清晰,反之亦然。目前常用的两种码流控制方式是VBR和CBR,VBR是动态比特率,CBR是固定比特率。那么他们是怎么来实现的呢?
我们首先看视频编码的目的,它是为了在有限的带宽中传输尽可能清晰的视频,我们以每秒25/帧的图像举例,25帧图像中定义了GOP组,目前主要是有I,B,P帧三种帧格式,I帧是关键帧,你可以想象它就是一幅JPEG压缩图像,而B,P帧是依靠I帧存在的,如果丢失了I帧,B,P帧是看不到图像的,B,P帧描述的不是实际的图像像素内容,而是每个相关像素的变化量,他们相对于I帧信息量会很小。GOP组是指一个关键帧I帧所在的组的长度,每个GOP组只有1个I帧。
我们再来看,一组画面的码流大小跟什么有关?当视频编码的压缩方式都一样,清晰度要求都一样的时候,GOP组的长度格式决定了码流的大小,例如:每秒25帧画面,GOP组长度为5,那么帧格式为IBPBP,那么1秒钟有5个I帧,10个B帧,10个P帧,如果GOP组长度为15,帧格式就是IBBPBBPBBPBBPBB,那么1秒钟内会有2个I帧和16个B帧和7个P帧,那么5个I帧比2个I帧占用的数据信息量大,所以GOP组的长度格式也决定了码流的大小。
最后,我们再来看VBR和CBR,对于VBR来说,它可以通过特殊的算法来控制一个GOP组的长度,也就是I帧的数量,当图像中运动图像多时,我可以把I帧数量加大,那么图像会很清晰,如果图像内元素基本静止时,它甚至可以把GOP组调到25的长度。那么根据前面的描述,可以想象这是一种为了确保图像质量的控制手段,它非常灵活,不死板,但是要求控制算法非常的精确,不精确会导致很多问题,例如码流突增后不降低,即使带宽足够,会导致图像出现问题。
CBR相反,它的带宽不变,码流不改变,一直都是一样的大小(会有少许的变化),图像运动量超过码流承受能力时,图像会出现边缘模糊的现象,它的目的是为了稳定带宽,它的实现方式就相对简单,码流不变就需要均衡IBP之间的关系,动态视频大时I帧小,BP大,运动视频小时I帧大,BP小。总之都是通过编解格式的算法来实现的。
总结一下,VBR码流控制方式可以降低图像动态画面少时候的带宽占用,CBR控制方式码流稳定,图像状态较稳定。他们为了解决的是不同需求下的不同应用。
帧间预测:
1) 先用匹配原则,在前一帧找到与当前帧(X,Y)相同位置匹配点的运动矢量(Vx,Vy)(ME)
2) 在前一帧中找到相同(最相似)点O{(X-Vx),(Y-Vy)}坐标,即用前一帧O点值fk-1 (X-Vx),(Y-Vy)对当前fk(X,Y)处的值进行帧间预测,预测值为FK(x,y)= fk-1(X-Vx),(Y-Vy)
3) E(x,y)残差= fk(X,Y)- FK(x,y)
H.264组成
1、网络提取层 (Network Abstraction Layer,NAL)
2、视讯编码层 (Video Coding Layer,VCL)
- a.H.264/AVC影像格式阶层架构
- b.Slice的编码模式
(1) I -slice: slice的全部MB都采用intra-prediction的方式来编码;
(2) P-slice: slice中的MB使用intra-prediction和inter-prediction的方式来编码,但每一个inter-prediction block最多只能使用一个移动向量;
(3) B-slice:与P-slice类似,但每一个inter-prediction block可以使用二个移动向量。B-slice的‘B’是指Bi-predictive(双向预测),除了可由前一张和后一张影像的I(或P、B)-slice外,也能从前二张不同影像的I(或P、B)-slice来做inter- prediction。
(4) SP-slice:即所谓的Switching P slice,为P-slice的一种特殊类型,用来串接两个不同bitrate的bitstream;
(5) SI-slice: 即所谓的Switching I slice,为I-slice的一种特殊类型,除了用来串接两个不同content的bitstream外,也可用来执行随机存取(random access)来达到网络VCR的功能 - c、画面内预测技术(Intra-frame Prediction)
- d、画面间预测技术(Inter-frame Prediction)
码流结构
H.264的功能分为两层,视频编码层(VCL)和网络提取层(NAL)VCL数据即被压缩编码后的视频数据序列。在VCL数据要封装到NAL单元中之后,才可以用来传输或存储。
SPS:序列参数集,作用于一系列连续的编码图像;
PSS:图像参数集,作用于编码视频序列中一个或多个独立的图像;
参数集是一个独立的数据单位,不依赖于参数集外的其他句法元素。一个参数集不对应某一个特定的图像或序列,同一序列参数集可以被多个图像参数集引用,同理,同一个图像参数集也可以被多个图像引用。只在编码器认为需要更新参数集的内容时,才会发出新的参数集。
NALU根据nal_unit_type的类型,可以分为:VCL的NAL单元和非VCL的NAL单元,详情如下:
其他零碎的知识
视频码率是视频数据(视频色彩量、亮度量、像素量)每秒输出的位数。一般用的单位是kbps。
由于不同的系统会有不同的模式,为了统一,规定在网络传输中使用大端模式,这就是网络字节序。
RTP协议:实时传送协议(Real-time Transport Protocol或简写RTP,也可以写成RTTP)是一个网络传输协议。RTP协议详细说明了在互联网上传递音频和视频的标准数据包格式。
RTCP协议:实时传输控制协议(Real-time TransportControl Protocol或RTP Control Protocol或简写RTCP)是实时传输协议(RTP)的一个姐妹协议。
RTSP协议:RTSP(RealTime Streaming Protocol)是用来控制声音或影像的多媒体串流协议。
RTSP发起/终结流媒体、RTP传输流媒体数据 、RTCP对RTP进行控制,同步。
RTMP协议:RTMP(the Real-time Messaging Protocol)协议作为客户端和服务器端的传输协议,这是一个专门为高效传输视频、音频和数据而设计的 TCP/IP 协议。
H.264中为什么使用离散余弦变换(DCT)
视频通信中的码率控制技术
昨天说到264中的模式选择,它的目地就是为码率控制服务。
即选择最优的宏块模式,以拉格郎日为优化模型。以控制码率。
编码控制的目标是在实际传输比特的限制下,以最小的计算复杂度获得尽可能好的图像质量。编码码率和计算复杂度,重建图像质量三者是不可分的。
评价标准: 主观和客观,主观指肉眼,客观指MSE和PSNR,通常认为PSNR平均值和最小PSNR值越大越好,各帧间PSNR值变化越小越好。
码率控制的主要环节: DCT变换,量化,缓冲区调整,分层编码(可分级编码和精细编码),网络状况反馈调节编码。
视频压缩标准基本上都用到了DCT变换,运动补偿编码和变长熵编码,由于对不同类型的帧(I B P)采用的编码方式不同,使得各帧图像编码后产生的比特数也不一样,根据传输速率,信道可分为两大类:变速率信道(VBR)和固定速率信道(CBR),无论编码的一的码流通过何种信道传输,视频编码器生成的码流都不能直接送到信道上传输,否则会千万信道或拥塞,或空闲,这都将降低信道的利用率。
为了使编码后的流速率能和信道速率相匹配,提高信道的利用率,在编码器和信道之间加一个缓冲区就非常必要,缓冲区的容量越大,码流将能更好地适应信道速率,重建图像的质量也会越高,但同时,也给解码端带来更大的时延,在实时视频通信中常常需要端对端的时延非常小,这就要求缓冲区尽可能小,缓冲区的容量和时延要求构成了一对矛盾,码率控制的目的就是为了解决这一矛盾。即在系统端对端的失真尽可能小情况下,满足时延的要求使得缓冲区不发生下溢上溢。
在视频编码过程中,决定输出码流比特率的直接因素为DCT变换后的量化参数Q(当cfg文件中,RDO未使能时,量化参数是不变的,即未使用率失真优化)。因此码率控制的方法是根据信道速率和缓冲区状态来调整量化参数Q,一般来说, 根据码率控制的级别,当前的编码器码率控制算法分为两大部分,基于帧和基于宏块的码率控制。控制的级别越低,控制的精度越高,算法复杂度越高。
JM8.6中帧内帧间模式的选择
在JM8.6中帧内与帧间模式的选择是其中非常重要的一部分, 模式选择的过程其实涵盖了H.264编码中的大部分内容. 从代码看来, 这一部分其实和码率控制中的代价函数计算是重叠在一起的, 在进行代价函数的计算过程中也就实现了模式的选择,代价计算完毕, 最优模式也就选择完毕.
1. 下面, 我们先回顾一下H.264中的帧内预测算法:
2. H.264中的帧间模式选择算法:
在帧间模式选中, 要进行的一个重要的过程就是运动估计(运动搜索)
3. 代价函数、
在上面我们也提到过, 帧内帧间模式的选择其实和代价的计算是重叠的. 在具体的JM中实现时, 其实对于代价的计算是分为两种情况的, 在帧内模式选择的过程中也提到过,主要分为RDO模式和非RDO模式. RDO指率失真优化。
H.264在运动搜索,参考帧择优, 模式决策三个方面运用了不同的RDO代价函数, 同时也将非RDO代价函数列为可选模式, 以满足不同的需要.
4. 具体代码实现
具体的代价函数如下:
代价函数主要包括MV_COST, REF_COST和MODE_COST, 而前两者在帧间模式选择时才存在, 换句话说,帧内模式选择时主要是计算MODE_COST.
4.1 运动矢量代价MV_ COST
运动矢量代价主要运用于运动搜索模块中, 为搜索点的择优提供了代价依据。H.264 直接运用了RDO运动搜索代价函数,其主要构成如下:
其中, SAD 是当前块和参考帧搜索位置块的差值, 如果用了哈达马变换则 SAD转化SATD;MV bits 是编码运动矢量MV 的比特数,由MV bits列表给出。
具体在JM8.6中的代码实现:
对比公式可以发现, 代码中MV_COST不包含SAD.只是计算的后面一部分
通过查看代码可以发现, MV_COST计算所使用的地方都是在运动搜索的函数中, 这也是可以理解的: 通过比较MV_COST来得到最佳的预测块.
其实仔细看代码我们可以发现, 在运动搜索时为了寻找最优块其实就是使用了上面的公式, 在代码中有计算SAD:
byte_abs与img->quad是类似,img->quad存储的是-255到255的平方,用于计算SSD,而byte_abs则是绝对值,用于计算SAD
在代码中都有类似的计算SAD的代码(有的地方使用SATD)
所以其实在运动搜索的函数中其实将计算得到的运动搜索储存在了mcost(或mv_cost)中,并且其中是包括SAD的.
在非RDO模式下搜索: 看下面的代码:
其中motion_cost是一个全局变量,记录着每种模式下在每一个参考帧下的每一个8x8块的运动搜索代价
接着看:在encode_one_macroblock函数中的帧间模式选择中:
//经过函数Partition...后motion_cost有每一个参考帧对应的运动搜索代价
在RDO模式下:
在RDO模式下比较特殊, 因为在这种情况下实际上是完成了编码, 直接生成了码流, 可以得到对应的码率(rate)
所以在RDO模式下没有计算运动搜索代价
对于MV_COST, 它搜索后的最优代价结果是保存在motion_cost中的
4.2 参考帧代价REF_ COST
参考帧代价用于多参考帧的择优, 采用RDO或非RDO 代价函数时,其定义有所差别。
(1) 非RDO 方式时:
其中,参考帧号为参考帧的编号,其他各变量的定义与MV_ COST 中一致。
(2) RDO 方式时:
其中, REF bit s 是编码参考帧号的比特数,由REF bit s 列表给出,其他变量与上述定义一致
具体在JM8.6中的实现:
同样也是没有考虑SAD先
可以看到, 这儿是分开RDO和非RDO的.与上面的 公式相符的.
同时, 我搜索了代码, 可以发现, 只有这一个地方计算REF_COST, 所以这个地方我们可以联系上面的运动搜索代价MV_COST, 可以发现其实两者最后都加到mcost变量中去了,用于参考帧的选择.
REF_COST用于参考帧的选择, RDO和非RDO模式下选出的参考帧不一样
对于MV_COST, 在RDO模式和非RDO模式下是一样的, 也就是说MV_COST的计算不分RDO模式或非RDO模式.
可以看出, 下面的这个循环根据MV_COST和REF_COST选择出了最佳参考帧:
准确来说上面REF_COST和MV_COST都是对应模式(16x16,16x8….)下针对某一块(8x8)的. 最后对于每一种模式都是要将所有的块累加起来比较大小的.
对于REF_COST, 它搜索后的最优代价(前向参考, 对于B帧还要考虑后向和双向)保存在fw_mcost中
总结一下, 上面的REF_COST和MV_COST都是两方面用处, 首先用于参考帧和运动矢量的搜索, 其次最优的参考帧代价和最优的运动矢量代价累加到模式代价中, 进行模式选择
4.3 模式代价MODE_COST
模式代价用于块模式的择优中, 在帧内预测块和帧间预测块的各模式选择过程中定义有所不同。
1. 帧内模式选择:
对于帧内, 主要是在I4MB和116MB之间进行选择.
对于亮度分量择优,非RDO 方式:
其中, SAD为当前块与预测块的差值,同样,如果用了哈达马变换则转化为S ATD 。
RDO 方式:(已编码)
其中, SSD 为当前块与重构块的差值;BL OCK bi t s 为某种预测模式下的残差块编码比特数,由熵编码结果给出。
对于色度分量帧内预测模式择优(色度分量只有帧内预测) , 因为对于帧间模式[色度块采用和亮度块同样的分割模式, 只是尺寸减半(水平和垂直方向都减半)], 并且只是在RDO方式下, 非RDO方式下没有考虑色度
SAD 为当前块与预测块的差值, 用了哈达马变换则转化为SATD。
我们来看代码方面的实现:
- 对于I4MB的模式代价计算:
代价的计算主要是在函数
中,最后的代价保存在cost中. 具体的计算时,将宏块16x16, 分为4个8x8块,利用
计算得到每一个8x8块的代价,然后累加起来得到整个宏块的代价.
对于8x8块是分为4个4x4块,利用
计算得到每一个4x4块的代价,然后累加起来得到一个8x8块的代价.即
- 对于一个4x4块, 在函数Mode_Decision_for_4x4IntraBlocks中,利用函数intrapred_luma计算出了9中预测模式下的预测值保存在img->mprr中.
接下来, 对于9种模式分别计算其代价,然后选择最优的模式,此时是要分RDO还是非RDO的:
非RDO方式下,
利用上面给出的公式计算代价, 代码如下:
计算中利用SATD函数,在这个函数中会根据配置文件中的设置来选择是否使用SAD还是SATD
最优的代价保存在min_cost中, 这样就可以得到整个宏块16个4x4块的代价之和, 也就是一个宏块在I4MB模式下的的代价.并且最优的I4MB中的模式保存在了best_ipmode中
RDO方式下,
先计算了一下, 残差, 保存在img->m7中,然后调用函数RDCost_for_4x4IntraBlocks,
(1) 在这个函数中,调用函数dct_luma对img->m7进行DCT,量化, 反DCT,反量化,将重建后的结果保存在enc_picture->imgY中.
(2) 计算出SSD,即distortion,
(3) 对该4x4块进行编码, 从而得到码流大小rate
(4) 最后利用公公式得到代价:
最后利用RDCost_for_4x4IntraBlocks返回的代价rdcost,在9种模式中比较得到最优的代价保存在min_rdcost中,同时最优模式保存在best_ipmode中.
在函数Mode_Decision_for_4x4IntraBlocks返回前还保存了最优模式在img->ipredmode中
最后根据在Mode_Decision_for_4x4IntraBlocks中求得的代价可以比较I4MB代价是不是比较小,如下:
对于116MB模式代价计算:
首先是非RDO方式:
(1)调用intrapred_luma_16x16函数,计算116MB的4种模式下的预测值,保存在img->mprr_2中
(2) 调用函数find_sad_16x16,计算SAD,将SAD作为最终的代价,根据SAD比较得到最优的116MB模式
其实这儿与下面的叙述结合起来看,可以发现,如果代码运动到上面的部分,那么非RDO方式下的帧内与帧间最佳模式已经选择出来了.非RDO下的帧内帧间预测选择是随着代码的从上而下的运行不断进行比较选择的.
RDO方式:
在RDCost_for_macroblocks函数中调用
查看Intra16x16_Mode_Decision可以看到与非RDO方式是一样的
但是RDO方式下, 还是要计算
2. 帧间模式选择:
对于亮度分量择优,非RDO 方式:
其中, MV _ COST、 RE F_ COST 分别为运动搜索、 参考帧择优后的最佳代价; MODE bit s 为编码模式号的比特数,由列表给出。
RDO 方式:
其中, SSD 为当前块与重构块的差值;BL OCK bit s 为某种块模式下的块编码比特数, 由熵编码结果给出, 包括残差块、 运动矢量和参考帧编码比特数。
下面看看具体JM8.6中的代码实现:
对于帧间模式需要在(16x16,16x8, 8x16,8x8,8x4,4x8和4x4)7种模式中选择一种, 对于RDO和非RDO差别比较大.
- 在非RDO方式下:
由于帧间的模式选择牵扯到运动搜索和参考帧搜索, 所以在计算模式代价的时候要考虑到这两方面. 在前面对MV_COST和REF_COST进行分析中,我们也提到过了, MV_COST和REF_COST是要作为帧间模式选择时代价函数的一个重要部分的.
- 首先是宏块级的选择: 16x16,16x8, 8x16
看代码我们可以发现, 其实MV_COST和REF_COST的选择是在模式选择的过程中完成的.
在计算一个宏块(16x16)在16x16,16x8,8x16三种模式下的代价的时候, mode16x16是一个块直接计算完, mode16x8是分两个块的和, mode8x16也是2个8x16块的和. 其中代码中的cost就是一整个宏块的总的代价和.
下面的代码很清晰的显示了在宏块级进行模式选择的过程.
- 然后是亚宏块级的选择: 8x8,8x4,4x8和4x4
亚宏块的选择与宏块级的步骤是类似的.
当前宏块的在亚宏块模式下的代价cost8x8与之前宏块级模式下的最优代价min_cost进行比较, 再选择最优
- 在RDO方式下:
在JM8.6中RDO方式下的模式选择包括帧内和帧间都是在一起进行选择的, 通过在函数RDCost_for_macroblocks中计算对应模式的RDO代价,进行比较得到最优的模式.从下面的代码中可以看出来:
下面我们深入到RDCost_for_macroblock函数中进行, 分析这些模式的比较, 由于对于帧内I4MB和116MB两种模式已经进行过分析, 所以这儿我们主要专注于帧间模式的选择:
首先, 调用函数
设置当前宏块的模式和参考帧信息(其实是设置currMB->b8mode[4])
上面针对不同的模式进行不同的计算.
我们先看对于亚宏块级(P8x8):
亚宏块级的RDO计算的代码不在这个地方,是和非RDO方式混合在一起的, 可以结合上面的对非RDO方式下对亚宏块级的分析来看>对于一个宏块的4个8x8块都要进行进行4种亚宏块级模式的选择:
调用了RDCost_for_8x8blocks函数, 对于RDCost_for_8x8blocks
由于在函数Partition...进行运动搜索时已经计算出了运动矢量mv保存在了img->all_mv中,所以这儿就可以根据mv直接找到预测值,进而在函数LumaPrediction4x4中完成预测值计算,保存在img->mpr中,然后在函数LumaResidualCoding8x8中计算得到残差,保存在img->m7中
接着再dct_luma函数中完成DCT变换,量化, 反DCT,反量化,然后将得到的重建值存在enc_picture->imgY中,同时也计算出了coef_cost, 这个coef_cost作为RDCost_for_8x8blocks函数中的cnt_nonz,表征残差中是否存在不为零的数. 经过这些操作后, 我们现在可以总结一下到底在RDO模式下的代价怎么计算的?
上面是RDCost_for_8x8blocks的返回值,我们可以发现, RDO模式下代价的计算是严格遵守了公式
失真distortion的计算是
是原始值与重建值的差
rate是对很多项进行编码后所占用的码流大小
RDCost_for_8x8blocks函数计算得到的代价保存在rdcost中,然后利用rdcost和min_rdcost比较
进而得到一个8x8块的最优P8x8中一种
这样我们就在RDO方式下, 得到了一个宏块中的4个8x8块的最优帧间模式.
同时我们要在这儿郑重说明一下: RDCost_for_8x8blocks函数与RDCost_for_macroblocks的不同在于, RDCost_for_8x8blocks只是在RDO方式下为一个8x8块在4种亚宏块模式中一种计算其代价,进行选择最佳模式,并且不考虑色度块,而RDCost_for_macroblocks是为一整个宏块(16x16)在(0,1-3,P8x8,I4MB,116MB)计算其RDO代价,然后选择一个最优模式,并且是考虑色度块的.
所以下RDCost_for_macroblocks中, 要对选择P8x8模式下的整个宏块重新计算其代价的.并且在本函数中只是调用函数设置一下参数
包括AC系数img->cofAC和重建值enc_picture->imgY及模式currMB->intra_pred_modes
- 宏块级(LumaResidualCoding):
对于帧间的模式(1-3)调用了函数LumaResidualCoding,在函数LumaResidualCoding中对宏块残差进行编码, 是将一个宏块分为4个8x8块分别进行编码, 在这个函数中调用了LumaResidualCoding8x8,与RDCost_for_8x8blocks函数类似,
在函数LumaResidualCoding8x8中, 对8x8块的4个4x4块分别进行预测,dct变换等,得到coef_cost
利用函数LumaPrediction4x4根据已经得到的mv来计算每一块的预测值,保存在img->mpr中,进而计算残差保存在img->m7中,然后利用这个残差调用函数dct_luma进行DCT, 量化,反DCT, 反量化, 之后得到重建值保存在enc_picture->imgY中.
其实到现在为止上面的分析都是在模式的选择都是在I4MB的9种中选择最佳,在116MB的4种中选择最佳, 在P8x8中的4种中选择最佳. 下面我们要在帧内和帧间所有7种模式(0,16x16,16x8,8x16,P8x8,I4MB和116MB)中选择最优.
先调用函数ChromaResidualCoding, 在这个函数中主要是计算色度的预测值, 保存在enc_picuture->imgUV中
(1) 计算失真distortion(包括亮度和色度)
(2)计算码率rate
最后计算代价
这样就得到了最优的代价及最优的模式.
4.5 JM8.6中模式选择
经过上面的分析, 下面我们对JM8.6中的模式选择做一个总结:
总起来说, 模式选择是这么一个过程:
分为RDO和非RDO两种情况, (我们假设帧内和帧间模式都允许)
1. 非RDO方式
(1) 在宏块级模式16x16, 16x8, 8x16中选择最佳模式(块划分),min_cost初始化为一个很大数,cost是一种宏块级模式的代价
最终,从上面的3种模式中选择到最佳模式:best_mode保存最佳模式,min_cost保存最佳代价
(2) 对整个宏块中的4个8x8块分别,在亚宏块级模式8x8,8x4,4x8,4x4中选择最佳模式,每一个8x8块的最佳模式保存在best8x8mode[4]中,最佳代价保存在min_cost8x8中, 最后4个8x8块的代价累加得到整个宏块的代价cost8x8中,
这样就可以将宏块的P8x8模式与宏块级模式进行比较了:
由于min_cost保存的是宏块级3种模式中的最佳代价所以和cost8x8比较,得到16x16,16x8,8x16和P8x8中的最优模式
同样best_mode保存最佳模式,min_cost保存最佳代价.
至此我们就得到了帧间预测模式中的最佳模式和对应的最佳代价
(3) 计算Direct的代价,与帧间最佳模式进行比较,得到最有模式:
(4) 进行帧内14MB的9种模式的选择,利用函数Mode_Decision_for_Intra4x4Macroblock,同时得到最优代价cost和整个宏块中16个4x4块对应的最优模式
img->ipredmode[pic_block_x][pic_block_y]= best_ipmode;
然后与Direct和帧间的最佳模式进行比较,得到三者中的最优模式
(5) 进行帧内116MB的4种模式的选择,先利用函数intrapred_luma_16x16计算得到4种模式下的预测值,保存在img->mprr_2中,然后利用 find_sad_16x16选择得到最佳模式116mode和对应代价cost,
然后cost与前面所有帧内帧间的最佳代价进行比较,从而得到最终的帧内和帧间的模式中的最佳模式:best_mode
最终利用函数SetModesAndRefframeForBlocks将best_mode 保存到当前宏块的mb_type
2.RDO方式
前提,所需要的运动矢量代价和对应的参考帧代价都计算出来了.
(1)在亚宏块级的4种模式中选择最优的模式,利用的是函数RDCost_for_8x8blocks计算一个8x8块的代价,这样最佳模式保存在了best8x8mode[4]中,这样就可以得到一个宏块的4个8x8块对应的最佳模式,
(2) 接下来在7种模式(0,16x16,16x8,8x16,P8x8,I4MB和116MB)中选择选择一种最优模式
其中I4MB是利用Mode_Decision_for_Intra4x4Macroblock得到9种I4MB帧内模式中得到最优模式后,作为I4MB
同样116MB是利用函数Intra16x16_Mode_Decision得到4种116MB帧内模式中的最优模式后作为116MB
在函数RDCost_for_macroblocks计算每种模式对应的代价,然后比较得到最优的模式.
由于RDO方式是要先对每种模式进行具体编码一次,得到对应的码率和失真后利用公式来计算得到的, 所以对于上面的7种模式都必须计算失真distortion(包括亮度和色度)和码率rate
另外对于P 帧亮度分量的特殊模式SKIP模式和B 帧亮度分量的特殊模式SKIP、 DIRECT 模式,其非RDO
代价:
SAD为当前块与参考帧相应位置块的差值,同样,如果用了哈达马变换则转化为SATD。
在代码中具体如下:
这个应该是对应B帧的Direct模式
H.264编码后码流的生成
H.264 比较全的编码框架
H.264中的PB帧编码
在针对连续动态图像编码时,将连续若干幅图像分成P,B,I三种类型,P帧由在它前面的P帧或者I帧预测而来,它比较与它前面的P帧或者I帧之间的相同信息或数据,也即考虑运动的特性进行帧间压缩。P帧法是根据本帧与相邻的前一帧(I帧或P帧)的不同点来压缩本帧数据。采取P帧和I帧联合压缩的方法可达到 更高的压缩且无明显的压缩痕迹。
在H.264编码中,I帧是内部编码帧,不需要参考其它帧,P帧需要前向的I帧作为参考,B是双向预测帧,需要前向和后向的I或者P帧作为其参考帧。由于帧与帧之间的参考关系比较复杂,彼此之间互相关联,对帧编码的简单并行是行不通的。因此,寻找共用参考帧的可编码帧成为实现帧级并行的关键。
假设编码序列中设置的B帧个数为2,其具体视频编码序列为IBBPBBPBBP…,依照I、P和B帧之间的参考关系,可以把连续的视频序列按照BBP的样式分割成一个个单元序列。经过分析可以看出一个BBP单元序列中的两个B帧由于共用前后的两个P帧作为参考帧,可以实施并行。同时,这个BBP单元序列中的P帧又作为下一个BBP单元序列中P帧的参考,因此前一单元序列中的两个B帧加上下一个单元序列中的P帧就可以实施三帧同时并行。以上是B帧设置参数为2时帧级并行的基本思路,可以得出原本执行一帧的时间现在可以用来执行三帧,理论加速比基本可以达到3,等于B帧设置 参数加1。
当设置编码序列中B帧个数可变时,帧级并行的线程数取决于B帧的个数,B帧数越大,并行加速比越高,在处理器足够的情况下,理论上获得的最大加速比等于B帧设置参数加1。由于在并行过程中,创建编码线程需要分配内存,线程之间的数据传输也会消耗资源,实际加速比会小于理论加速比,再加上处理器个数资源的限制,会在某个B帧的个数上获得一个峰值加速比.
H.264中运动矢量和多参考帧运动估计
一个宏块中的运动矢量
每一个块具有一个运动矢量,这句话我们可以这么理解, 块的大小不定, 从16x16~4X4,所以,最多可以有16个不同的MV,但是
所以对于一个宏块肯定是携带16个MV,只是可能其中一些MV是相同的.
多参考帧运动估计
关于H264帧间色度块预测的问题
老毕书上在第6章这么写的:
所以帧间色度块不用预测,
JM86如何设置编码器分片参数:
关于SliceMode中的CALLBACK模式的解释
JM8.6中I帧,P帧的理解
通过下面的代码我们可以发现
对于一帧, 如果设置这一帧进行帧内编码,即intra=1,那么这一帧不会再进行帧间预测,这一帧即为I帧, 但是, 如果设置某一帧要进行帧间预测,即intra=0,那么这个时候帧内预测和帧间预测都要进行, 从上面的代码可以看出, 进行帧间预测是包含着一个if判断语句中的, 而帧内预测是没有条件的, 是任何情况下都要进行的. 之后jm代码会比较帧内预测与帧间预测两者之间的优劣, 选择一种使率率失真函数最优的方法. 换句话说, 可以认为intra其实是一个标志(flag),用来标示是否要进行帧间预测. 同样我们也可以看到,对于p帧中的宏块, 帧内预测和帧间预测都要进行的, 最后也有可能使用帧内预测. 所以p帧内的宏块包括帧内预测和帧间预测的宏块。
对于上面需要一些更正
M8.6中对数据分割的一点解释
分析currslice->partarr[partmap[SE_MVD]] (关于数据分割的实现)
Currslice指当前slice
Partarr是一个data partition数组
Partmap :const int* partmap =assignse2partition[input->partition_mode];
Int * assignse2partition[2] ;
率失真曲线
率失真(RD)曲线 反映了不同编码器的编码性能好坏。
一般RD曲线都是以码率(Kbps)做为横坐标,以PSNR(dB)作为纵坐标做出来一条曲线,曲线上的点一般是采用QP=28,32,36,40这四个QP下的编码码率和编码质量 (QPi, butrate,psnr)。
曲线点越高,表明性能越好。
JM8.6中重要结构体说明
定义这些枚举类型, 主要是为了使代码更加清晰易读, 便于识别一些变量的含义
数据分区方式:PAR_DP_TYPE[PAR_DP_1(不使用数据分区), PAR_DP_3(使用ABC3数据分区)]
输出文件的类型:PAR_OF_TYPE[PAR_OF_ANNEXB,PAR_OF_RTP]
编码的方式: CodingType [FRAME_CODING,FIELD_CODING, ADAPTIVE_CODING]
句法元素的类型: SE_type[SE_HEADER, SE_PTYPE,SE_MBTYPE, SE_REFFRAME, SE_INTRAPREDMODE, SE_MVD ….]
比特位类型: BitCountType[BITS_HEADER,
BITS_TOTAL_MB,
BITS_MB_MODE, //++宏块类型、宏块模式编码比特数(writeMBHeader函数)
BITS_INTER_MB, //++ 帧间宏块的参考帧、运动向量编码比特数(writeMotionInfo2NAL函数)
BITS_CBP_MB, //++宏块 CBP编码比特数(writeCBPandLumaCoeff函数)
BITS_COEFF_Y_MB, //++宏块亮度残差编码比特数(writeCBPandLumaCoeff函数)
BITS_COEFF_UV_MB, //++宏块色度残差编码比特数(writeChromaCoeff函数)
BITS_DELTA_QUANT_MB, //++宏块量化参数增量编码比特数(writeCBPandLumaCoeff函数)
MAX_BITCOUNTER_MB
];
图像结构类型: PictureStructure[FRAME, TOP_FIELD,BOTTOM_FIELD]
片结构类型: SliceType[ P_SLICE = 0, B_SLICE,I_SLICE, SP_SLICE, SI_SLICE];
总结一下,JM8.6中比较重要的源代码文件
[block.c]用于处理一个块
void
intrapred_luma (int img_x, int img_y, int *left_available, int *up_available, int *all_available)
void
intrapred_luma_16x16 ()
int
dct_luma_16x16 (int new_intra_mode)
int
dct_luma (int block_x, int block_y, int *coeff_cost, int old_intra_mode)
int
dct_chroma (int uv, int cr_cbp)
[decoder.c] "decoders in theencoder
void
decode_one_b8block (int decoder, int mbmode, int b8block, int b8mode, int b8ref)
void
decode_one_mb (int decoder, Macroblock *currMB)
void
Get_Reference_Block (byte **imY, int block_y, int block_x, int mvhor, int mvver, byte **out)
byte
Get_Reference_Pixel (byte **imY, int y_pos, int x_pos)
void
UpdateDecoders ()
void
DecOneForthPix (byte **dY, byte ***dref)
void
compute_residue_b8block (int b8block, int i16mode)
void
compute_residue_mb (int i16mode)
void
Build_Status_Map (byte **s_map)
void
Error_Concealment (byte **inY, byte **s_map, byte***refY)
void
Conceal_Error (byte **inY, int mb_y, int mb_x, byte***refY, byte **s_map)
[image.c] Code one image/slice
void
code_a_picture (Picture *pic)
void
frame_picture (Picture *frame)
static int
writeout_picture (Picture *pic)
static int
picture_structure_decision (Picture *frame,Picture *top, Picture *bot)
static void
distortion_fld (float *dis_fld_y, float *dis_fld_u, float *dis_fld_v)
static void
find_snr ()
static void
find_distortion ()
static void
frame_mode_buffer (int bit_frame, float snr_frame_y, float snr_frame_u, float snr_frame_v)
static void
init_frame ()
static void
put_buffer_frame ()
static void
copy_motion_vectors_MB ()
static void
CopyFrameToOldImgOrgVariables(Sourceframe *sf)
staticSourceframe *
AllocSourceframe (int xs, int ys)
static void
FreeSourceframe (Sourceframe *sf)
static void
ReadOneFrame (int FrameNoInFile, int HeaderSize, int xs, int ys, Sourceframe *sf)
static void
writeUnit (Bitstream *currStream, int partition)
static int
CalculateFrameNumber ()
int
encode_one_frame ()
static void
init_dec_ref_pic_marking_buffer ()
static void
GenerateFullPelRepresentation (pel_t **Fourthpel, pel_t *Fullpel, int xsize, int ysize)
void
UnifiedOneForthPix (StorablePicture *s)
Boolean
dummy_slice_too_big (int bits_slice)
void
copy_rdopt_data (int bot_block)
static void
GenerateFieldComponent (char *src, char *top, char *bot, int xs, int ys)
[lencod.c]主函数所在文件
void
Init_Motion_Search_Module ()
void
Clear_Motion_Search_Module ()
int
main (int argc, char **argv)
void
report_stats_on_error ()
void
init_poc ()
void
CAVLC_init ()
void
init_img ()
void
free_img ()
Picture*
malloc_picture ()
void
free_picture (Picture *pic)
int
init_global_buffers ()
void
free_global_buffers ()
int
get_mem_mv (int *******mv)
int
get_mem_ACcoeff (int *****cofAC)
void
combine_field ()
void
process_2nd_IGOP ()
void
SetImgType ()
[macroblock.c] Process one macroblock
void
set_MB_parameters (int mb_addr)
void
proceed2nextMacroblock ()
void
start_macroblock (int mb_addr, int mb_field)
void
terminate_macroblock (Boolean *end_of_slice, Boolean*recode_macroblock)
int
slice_too_big (int rlc_bits)
void
OneComponentLumaPrediction4x4 (int *mpred, int pic_pix_x, int pic_pix_y, int *mv, int ref, StorablePicture**list)
void
copyblock4x4 (int *mpred, int block[BLOCK_SIZE][BLOCK_SIZE])
void
LumaPrediction4x4 (int block_x, int block_y, int p_dir, int fw_mode, int bw_mode, int fw_ref_idx, int bw_ref_idx)
int
LumaResidualCoding8x8 (int *cbp, int *cbp_blk, int block8x8, int p_dir, int fw_mode, int bw_mode, int fw_refframe, int bw_refframe)
void
SetModesAndRefframe (int b8, int *p_dir, int *fw_mode, int *bw_mode, int *fw_ref, int *bw_ref)
void
LumaResidualCoding ()
void
OneComponentChromaPrediction4x4 (int *mpred, int block_c_x, int block_c_y, int ******mv, int list_idx, int ref, int blocktype, int uv)
void
IntraChromaPrediction4x4 (int uv, int block_x, int block_y)
void
ChromaPrediction4x4 (int uv, int block_x, int block_y, int p_dir, int fw_mode, int bw_mode, int fw_ref_idx, int bw_ref_idx)
void
ChromaResidualCoding (int *cr_cbp)
void
IntraChromaPrediction8x8 (int *mb_up, int *mb_left, int *mb_up_left)
int
ZeroRef (Macroblock *currMB)
int
MBType2Value (Macroblock *currMB)
int
writeIntra4x4Modes (int only_this_block)
int
B8Mode2Value (int b8mode, int b8pdir)
int
writeMBHeader (int rdopt)
void
write_terminating_bit (short bit)
int
writeChromaIntraPredMode ()
void
set_last_dquant ()
void
write_one_macroblock (int eos_bit)
int
BType2CtxRef (int btype)
int
writeReferenceFrame (int mode, int i, int j, int fwd_flag, int ref)
int
writeMotionVector8x8 (int i0, int j0, int i1, int j1, int refframe, int list_idx, int mv_mode)
int
writeMotionInfo2NAL ()
int
writeChromaCoeff ()
int
writeLumaCoeff4x4_CABAC (int b8, int b4, int intra4x4mode)
int
writeLumaCoeff8x8 (int block8x8, int intra4x4mode)
int
writeCBPandLumaCoeff ()
int
predict_nnz (int i, int j)
int
predict_nnz_chroma (int i, int j)
int
writeCoeff4x4_CAVLC (int block_type, int b8, int b4, int param)
int
find_sad_16x16 (int *intra_mode)
[mb_access.c]
int
mb_is_available (int mbAddr, int currMbAddr)
void
CheckAvailabilityOfNeighbors ()
void
get_mb_block_pos (int mb_addr, int *x, int *y)
void
get_mb_pos (int mb_addr, int *x, int *y)
void
getNonAffNeighbour (unsigned int curr_mb_nr, int xN, int yN, int luma, PixelPos *pix)
void
getAffNeighbour (unsigned int curr_mb_nr, int xN, int yN, int luma, PixelPos *pix)
void
getNeighbour (int curr_mb_nr, int xN, int yN, int luma, PixelPos *pix)
void
getLuma4x4Neighbour (int curr_mb_nr, int block_x, int block_y, int rel_x, int rel_y, PixelPos *pix)
void
getChroma4x4Neighbour (int curr_mb_nr, int block_x, int block_y, int rel_x, int rel_y, PixelPos *pix)
[mv-search.c]
void
SetMotionVectorPredictor (int pmv[2], int ***refPic, int ****tmp_mv, int ref_frame, int list, int block_x, int block_y, int blockshape_x, int blockshape_y)
void
InitializeFastFullIntegerSearch ()
void
ClearFastFullIntegerSearch ()
void
ResetFastFullIntegerSearch ()
void
SetupLargerBlocks (int list, int refindex, int max_pos)
void
SetupFastFullPelSearch (int ref, int list)
void
Init_Motion_Search_Module ()
void
Clear_Motion_Search_Module ()
int
FullPelBlockMotionSearch (pel_t **orig_pic, int ref, int list, int pic_pix_x, int pic_pix_y, int blocktype, int pred_mv_x, int pred_mv_y, int *mv_x, int *mv_y, int search_range, int min_mcost, double lambda)
int
FastFullPelBlockMotionSearch (pel_t **orig_pic, int ref, int list, int pic_pix_x, int pic_pix_y, int blocktype, int pred_mv_x, int pred_mv_y, int *mv_x, int *mv_y, int search_range, int min_mcost, double lambda)
int
SATD (int *diff, int use_hadamard)
int
SubPelBlockMotionSearch (pel_t **orig_pic, int ref, int list, int pic_pix_x, int pic_pix_y, int blocktype, int pred_mv_x, int pred_mv_y, int *mv_x, int *mv_y, int search_pos2, int search_pos4, int min_mcost, double lambda)
int
BlockMotionSearch (int ref, int list, int mb_x, int mb_y, int blocktype, int search_range, double lambda)
int
BIDPartitionCost (int blocktype, int block8x8, int fw_ref, int bw_ref, int lambda_factor)
int
GetSkipCostMB (double lambda)
void
FindSkipModeMotionVector ()
int
Get_Direct_Cost8x8 (int block, double lambda)
int
Get_Direct_CostMB (double lambda)
void
PartitionMotionSearch (int blocktype, int block8x8, double lambda)
void
Get_Direct_Motion_Vectors ()
[rdopt.c]
void
clear_rdopt ()
void
init_rdopt ()
void
UpdatePixelMap ()
int
CheckReliabilityOfRef (int block, int list_idx, int ref, int mode)
double
RDCost_for_4x4IntraBlocks (int *nonzero, int b8, int b4, int ipmode, double lambda, double min_rdcost, int mostProbableMode)
int
Mode_Decision_for_4x4IntraBlocks (int b8, int b4, double lambda, int *min_cost)
int
Mode_Decision_for_8x8IntraBlocks (int b8, double lambda, int *cost)
int
Mode_Decision_for_Intra4x4Macroblock (double lambda, int *cost)
double
RDCost_for_8x8blocks (int *cnt_nonz, int *cbp_blk, double lambda, int block, int mode, int pdir, int ref, int bwd_ref)
int
I16Offset (int cbp, int i16mode)
void
SetModesAndRefframeForBlocks (int mode)
void
Intra16x16_Mode_Decision (Macroblock *currMB, int *i16mode)
void
SetCoeffAndReconstruction8x8 (Macroblock *currMB)
void
SetMotionVectorsMB (Macroblock *currMB, int bframe)
int
RDCost_for_macroblocks (double lambda, int mode, double *min_rdcost)
void
store_macroblock_parameters (int mode)
void
set_stored_macroblock_parameters ()
void
SetRefAndMotionVectors (int block, int mode, int pdir, int fwref, int bwref)
int
field_flag_inference ()
void
encode_one_macroblock ()
[rdopt_coding_state.c]
void
delete_coding_state (CSptr cs)
CSptr
create_coding_state ()
void
store_coding_state (CSptr cs)
void
reset_coding_state (CSptr cs)
[vlc.c]
int
ue_v (char *tracestring, int value, DataPartition *part)
int
se_v (char *tracestring, int value, DataPartition *part)
int
u_1 (char *tracestring, int value, DataPartition *part)
int
u_v (int n, char *tracestring, int value, DataPartition *part)
void
ue_linfo (int ue, int dummy, int *len, int *info)
void
se_linfo (int se, int dummy, int *len, int *info)
void
cbp_linfo_intra (int cbp, int dummy, int *len, int *info)
void
cbp_linfo_inter (int cbp, int dummy, int *len, int *info)
void
levrun_linfo_c2x2 (int level, int run, int *len, int *info)
void
levrun_linfo_inter (int level, int run, int *len, int *info)
void
levrun_linfo_intra (int level, int run, int *len, int *info)
int
symbol2uvlc (SyntaxElement *sym)
int
writeSyntaxElement_UVLC (SyntaxElement *se,DataPartition *this_dataPart)
int
writeSyntaxElement_fixed (SyntaxElement *se,DataPartition *this_dataPart)
int
writeSyntaxElement_Intra4x4PredictionMode(SyntaxElement *se, DataPartition *this_dataPart)
int
writeSyntaxElement2Buf_UVLC (SyntaxElement *se,Bitstream *this_streamBuffer)
void
writeUVLC2buffer (SyntaxElement *se, Bitstream*currStream)
int
writeSyntaxElement2Buf_Fixed (SyntaxElement *se,Bitstream *this_streamBuffer)
int
symbol2vlc (SyntaxElement *sym)
int
writeSyntaxElement_VLC (SyntaxElement *se,DataPartition *this_dataPart)
int
writeSyntaxElement_NumCoeffTrailingOnes(SyntaxElement *se, DataPartition *this_dataPart)
int
writeSyntaxElement_NumCoeffTrailingOnesChromaDC(SyntaxElement *se, DataPartition *this_dataPart)
int
writeSyntaxElement_TotalZeros (SyntaxElement *se,DataPartition *this_dataPart)
int
writeSyntaxElement_TotalZerosChromaDC(SyntaxElement *se, DataPartition *this_dataPart)
int
writeSyntaxElement_Run (SyntaxElement *se,DataPartition *this_dataPart)
int
writeSyntaxElement_Level_VLC1 (SyntaxElement *se,DataPartition *this_dataPart)
int
writeSyntaxElement_Level_VLCN (SyntaxElement *se, int vlc, DataPartition *this_dataPart)
void
writeVlcByteAlign (Bitstream *currStream)
[cabac.c]
void
unary_bin_encode(EncodingEnvironmentPtr eep_dp, unsigned int symbol, BiContextTypePtr ctx, int ctx_offset)
void
unary_bin_max_encode(EncodingEnvironmentPtr eep_dp, unsigned int symbol, BiContextTypePtr ctx, int ctx_offset, unsigned int max_symbol)
void
unary_exp_golomb_level_encode(EncodingEnvironmentPtr eep_dp, unsigned int symbol, BiContextTypePtr ctx)
void
unary_exp_golomb_mv_encode(EncodingEnvironmentPtr eep_dp, unsigned int symbol, BiContextTypePtr ctx, unsigned int max_bin)
void
cabac_new_slice ()
void
CheckAvailabilityOfNeighborsCABAC ()
MotionInfoContexts *
create_contexts_MotionInfo (void)
TextureInfoContexts *
create_contexts_TextureInfo (void)
void
delete_contexts_MotionInfo(MotionInfoContexts *enco_ctx)
void
delete_contexts_TextureInfo(TextureInfoContexts *enco_ctx)
int
writeSyntaxElement_CABAC(SyntaxElement *se, DataPartition*this_dataPart)
void
writeFieldModeInfo_CABAC (SyntaxElement*se, EncodingEnvironmentPtr eep_dp)
void
writeMB_skip_flagInfo_CABAC(SyntaxElement *se,EncodingEnvironmentPtr eep_dp)
void
writeMB_typeInfo_CABAC (SyntaxElement*se, EncodingEnvironmentPtr eep_dp)
void
writeB8_typeInfo_CABAC (SyntaxElement*se, EncodingEnvironmentPtr eep_dp)
void
writeIntraPredMode_CABAC (SyntaxElement*se, EncodingEnvironmentPtr eep_dp)
void
writeRefFrame_CABAC (SyntaxElement *se,EncodingEnvironmentPtr eep_dp)
void
writeDquant_CABAC (SyntaxElement *se,EncodingEnvironmentPtr eep_dp)
void
writeMVD_CABAC (SyntaxElement *se,EncodingEnvironmentPtr eep_dp)
void
writeCIPredMode_CABAC (SyntaxElement*se, EncodingEnvironmentPtr eep_dp)
void
writeCBP_BIT_CABAC (int b8, int bit, int cbp,Macroblock *currMB, int inter,EncodingEnvironmentPtr eep_dp)
void
writeCBP_CABAC (SyntaxElement *se,EncodingEnvironmentPtr eep_dp)
void
write_and_store_CBP_block_bit(Macroblock *currMB,EncodingEnvironmentPtr eep_dp, int type, int cbp_bit)
void
write_significance_map (Macroblock*currMB, EncodingEnvironmentPtr eep_dp, int type, int coeff[], int coeff_ctr)
void
write_significant_coefficients (Macroblock*currMB, EncodingEnvironmentPtr eep_dp, int type, int coeff[])
void
writeRunLevel_CABAC (SyntaxElement *se,EncodingEnvironmentPtr eep_dp)
void
exp_golomb_encode_eq_prob(EncodingEnvironmentPtr eep_dp, unsigned int symbol, int k)
JM8.6中encode_one_macroblock函数的运作流程[宏块代价函数的使用或帧间帧内模式选择流程]
在JM中最重要, 最核心的一个函数就是encode_one_macroblock, 这个函数是整个编码器的核心, 其实, 通过读源码,可以发现,encode_one_macroblock中的大部分工作是在进行模式的选择, 下面我们跟着代码走一下, 看看具体的流程: (我们假设帧间帧内同时都允许)
1. 帧间三种帧间宏块级模式( 16×16, 16×8,8×16) 循环, 进行择优
同时将min_cost初始化为最大值,将best_mode初始化为1(16x16)
1.1. 对三种模式下的每一个块进行计算最佳代价
16*16 分割方式只需要计算一次,16*8 和8*16 分割方式需要计算两次
1.1.1 运动搜索
在函数PartitionMotionSearch中对模式mode进行运动搜索, 在搜索中利用MV_COST计算搜索代价, 得到该模式下的最优代价保存在motion_cost中及最佳MV保存在 img->all_mv中.
其中也包含对非RDO 方式下P 帧SKIP 模式的择优
1.1.2 参考帧择优
在计算时利用代价函数REF _COST得到参考帧代价, 并且加上对应模式下对应参考帧的搜索代价, 总起来作为代价进行参考帧的选择(在利用REF_COST时根据是否是RDO进行区分,如果使用的是RDO方式, 则选择该方式下的最优参考帧.
另外对于B帧的情况要考虑后向参考和双向参考的情况.
对于P帧的情况,也就是前向参考的情况
只将前向参考代价加入到cost中,cost是以整个宏块的所有代价和
1.2 根据一整个宏块的代价选择出最优的代价
2. 帧间亚宏块模式( P8x8模式:8x8,8x4,4x8,4x4)循环,进行择优
对于一个整宏块是分为4个8x8块, 然后每个8x8块进行P8x8模式的选择
进行P8x8选择时, 先初始化min_cost8x8(非RDO)为最大,min_rdcost(RDO)为最大
2. 1 运动搜索,
与宏块级类似
代价函数用MV _ COST (包含对非RDO 方式下P 帧SKIP 模式的择优) ;
step2. 2 参考帧择优, 代价函数用 REF _COST ;
step2. 3 块模式择优, 代价函数用 MODE _COST ;
step2. 4 对于非RDO 方式,将step2. 3 的择优结果与step1中的最佳大模式进行比较决策,选出最优
模式。
step3 所有模式择优
step3. 1 RDO 方式下,对三种帧间大块模式、最佳P8 模式、 两种帧内预测模式以及SKIP、 DIRECT
模式进行RDO 模式代价MODE _COST 的比较择优:
step3. 1. 1 四种色度块帧内预测模式循环;
step3. 1. 2 设置运动预测和参考帧模式;
step3. 1. 3 计算七种模式的RDO 模式代价MODE_ COST ,进行比较决策。
step3. 2 非RDO 方式下,将step2 的择优结果与DIRECT模式、 帧内预测模式进行比较择优:
step3. 2. 1 获得DIRECT模式下的代价MODE_ COST ,进行比较择优;
step3. 2. 2 进行4×4 和16×16 的帧内预测, 用MODE_COST 对预测模式择优。
在JM代码中共有3个方法进行RDCost的计算
主要为:
- RDCost_for_4x4IntraBlocks,
RDCost_for_4x4IntraBlocks被调用的函数
可以发现, RDCost_for_4x4IntraBlocks只被一个函数直接调用, 从调用流程我们就可精确的知道RDCost_for_4x4IntraBlocks的用处:帧内模式I4MB在RDO方式下的计算函数, 主要是计算一个4x4块的RDO代价
- RDCost_for_8x8blocks,
该函数一个宏块的4个8x8块在亚宏块模式下进行模式选择的时候, 计算其RDO代价, 根据求得的代价对当前的8x8块选择最佳的P8x8模式
- RDCost_for_macroblocks
这个函数很好理解, 对于一个宏块,分别计算在16x16,16x8, 8x16,P8x8,I4MB,I16MB模式下的RDO代价,然后选择最佳的模式, 由于包括的帧内和帧间的所有模式, 所以这个函数可以实现帧内和帧间模式的选择
2011年4月24日9:31:52
JM8.6中的RDO代价函数的分析: RDCost_for_8x8blocks, RDCost_for_macroblocks,RDCost_for_4x4IntraBlocks
在JM8.6中有三种计算RDO模式下代价的函数,分别为RDCost_for_8x8blocks,RDCost_for_macroblocks和RDCost_for_4x4IntraBlocks
帧内:RDCost_for_4x4Intrablocks是用于帧内I4MB模式的选择,
这个函数是在函数Mode_Decision_for_4x4IntraBlocks
中进行调用的, Mode_Decision_for_4x4IntraBlocks函数就是为了对于4x4块进行帧内9种模式的选择.在这个函数中.首先利用函数intrapred_luma计算出了9种模式下对应的预测值,存储在数组img->mprr[9*16*16]中,然后对9种模式进行for循环,在该for循环中,在进行9种模式的代价比较计算的时候,分RDO和非RDO两种情况,
在非RDO模式下,只考虑了SATD,最后的最优代价保存在 min_cost变量中,
在RDO模式下,由于考虑了到了rate,所以在函数RDCost_for_4x4Intrablocks中进行计算.在进入RDCost_for_4x4Intrablocks之前,先保存当前的编码状态,因为在RDCost_for_4x4Intrablocks函数中要进行编码,解码(重构)的过程.在RDCost_for_4x4Intrablocks函数中,因为是为了进行帧内9种模式的选择,所以Rate计算的时候只会考虑到了模式和亮度残差系数的编码比特数.并且对于distortion利用的是SSD,并且是只考虑了亮度分量
在代码中的表现如下:
在函数的最后利用公式计算出RDO下的代价:
帧间:RDCost_for_8x8blocks 计算的是P8x8 的最优模式代价,用于与其他 inter 模式比较得出最佳 inter 模式,
该函数在encode_one_macroblock函数中的计算亚宏块模式下的一个8x8块的rdcost代价
//+++RDCost_for_8x8blocks是在4种亚宏块模式中选择最佳的模式(代价)
//+++准确来说RDCost_for_8x8blocks主要是得到编号为block的8x8块,在mode模式下的代价
//+++RDCost包括SSD和rate两部分:
//+++(1)SSD,在此处只计算了亮度分量的失真
//+++(2)rate,由于是在帧间的亚宏块模式中进行选择,所以在这儿包括了模式(b8value),运动矢量(writeReferenceFrame和writeMotionVector8x8)和亮度残差
具体的代码中如下:
在函数的最后
帧内与帧间比较:RDCost_for_macroblocks 是在最佳 inter 与 intra 之间选择。
这个函数是在encode_one_macroblock函数中进行调用的.主要是在帧间16x16, 16x8, 8x16, P8x8, 帧内Intra4x4, Intra16x16这些模式间进行代价的计算
//+++为了计算代价rdcost,需要计算distortion和rate两部分:
//+++(1)这个函数中对应distortion的计算使用了亮度分量和色度分量
//+++(2)由于这儿是在帧内和帧间模式间进行选择,所以rate考虑了宏块头数据, 模式, 运动向量, 亮度残差数据以及色度残差数据。
在代码中
在函数最后
摘自:JM8.6核心代码研究
对于计算SSD来说, 有时程序只考虑了亮度分量, 例如RDCost_for_8x8blocks()函数, 有时候, 程序同时考虑了亮度分量和两个色度分量, 例如RDCost_for_macroblock()函数。Rate 表示编码所需要的比特数。程序将调用熵编码函数进行熵编码操作以确定Rate 值。在不同情况下, Rate 有不同的含义。在 Rdcost_for_8x8blocks()函数中, 为了进行帧间的模式选择,Rate的计算过程将编码下列的信息: 模式, 运动矢量和亮度残差数据。在RDCost_for_4x4IntraBlocks()函数中,为了进行亮度帧内预测模式的选择, Rate只包括模式和亮度残差系数的编码比特数。 而在RDCost_for_macroblock()函数中, 由于同时进行了帧内, 帧间, 亮度和色度的模式选择, Rate 将同时包含以下部分: 宏块头数据, 模式, 运动向量, 亮度残差数据以及色度残差数据。
配置文件的理解:
Files输入/输出文件配置
EncoderControl 编码控制
ALU第一字节就是包括3个语法结构: forbidden_zero_bit(1),nal_ref_idc(2),nal_unit_type(5),加起来正好一个字节,
<<H.264中 4x4 块的快速帧内预测算法>>
作者:王启文,黄东军
目前,对帧内预测的研究已取得很多成果,如快速三步法[1]。该算法利用相邻预测方向的相关性,选择性地计算帧内方向预测模式,以达到减少率失真优化计算量的目的,但它对编码时间的节约效果不理想。文献[2]利用子块边缘的变化方向从预选模式中选取最可能的预测方向,该算法采用边缘梯度直方图法,在几种最可能的预测方向中选取最佳预测方向,在视频场景较复杂的情况下,其视频压缩效果不佳。文献[3]对文献[2]算法进行改进,
但对预测正确率的提高效果不明显。文献[4]提出自适应阈值快速算法,利用邻块相关性为当前块的率失真设置一个阈值,提前判断当前块是否为帧内块,但视频图像的动态变化性导致其阈值很难预测。鉴于此,本文提出一种新的预测方向度量方法。
在 H.264 标准中,通过 RDO 技术,对所有帧内预测模式进行遍历,并选择一种最优预测模式。帧内预测块的预测模式不是直接进行熵编码,而是将其与最可能的模式相减后,
进行熵编码。最可能模式的符号为 most-probable-mode[4],它是由当前块的左边块(图 3 中的 A)和上边块(图 3 中的 B)的预测模式得到的。当前块的预测模式为 most-probable-mode
时,只要用一个比特来表示预测模式,与选用其他模式相比,其所用比特数最少。
本文算法充分利用了 4×4 块预测模式的方向特性,以及相邻块和相邻方向预测模式的相关性。先对 4 个方向性强的方向预测模式进行度量,即垂直(模式 0)、水平(模式 1)、对角线左下(模式 3)、对角线右下(模式 4)。然后从度量结果中选出候选预测模式,并计算当前块是否具有方向性。如果具有方向性,则根据相邻方向预测模式相关性,将与候选模式相邻的预测模式作为候选预测模式。否则,把模式 2 选为候选模式。最后根据相邻块的相关性,把相邻块的预测模式作为候选模式。
参考文献
[1] Cheng Chao-Chung, Chang Tian-Sheuan.Fast Three Step Intra Prediction Algorithm for 4×4 Blocks in H.264[C]//Proc. ofIEEE International Symposium on Circuits and Systems. Kobe, Japan:[s. n.],2005.
[2] Pan Feng, Lin Xiao. Fast Mode Decisionfor Intra Prediction[J]. IEEE Trans. on Circuits and Systems for Video Technology,2005, 15(7): 813-822.
[3] 李世平, 蒋刚毅, 郁梅. 快速帧内预测模式选择新方法[J].电子学报, 2006, 34(1): 141-146.
[4] Kim B G. Fast Selective Intra-modeSearch Algorithm Based on Adaptive Thresholding Scheme for H.264/AVC Encoding[J].IEEE Trans. on Circuits and Systems for Video Technology, 2008, 18(1): 127-133.
[5] Lim Keng-Pang. Text Description ofJoint Model Reference Encoding Methods and Decoding Concealment Methods[Z].2005.
<<快速帧内预测模式选择新方法>>
作者:李世平 蒋刚毅 郁梅
<<基于 SAD 和 SATD 的 H.264 快速帧内预测算法 >>
作者:谢翠兰,郑艺玲
H.264 帧内预测模式确定过程
H.264 帧内编码中,采用亮度和色度相结合的率失真优化(Rate-Distortion Optimization, RDO)判优准则, RDO判决准则,通过遍历所有预测模式,寻找率失真代价 RDCost 最小的作为最优预测模式。率失真代价公式为
RDCost=SSD+λ×Rate
其中,SSD 表示当前块与重构块的平方差之和;λ 为量化参数 QP 的函数;Rate表示熵编码后的码率。
H.264中确定最优帧内预测模式的过程如下:
(1) 确定 Intra_16×16 方式下的最优预测模式:
1)用 4 种预测模式对待编码宏块进行预测;
2)对预测残差宏块进行哈达马变换,并计算编码代价;
3)选择编码代价最小的模式作为Intra_16×16 方式下的最优预测模式。
(2)结合色度编码,计算待编码宏块在Intra_16 × 16 方式下的率失真代价RDCostI16。
(3)确定 Intra_4 × 4 方式下的最优预测模式。
将待编码宏块划分为 16 个 4×4 块,分别确定每个 4×4块的最优预测模式:
1)用 9 种预测模式对 4×4 块进行预测;
2)对预测残差块进行 DCT 变换/量化、 反DCT 变换/反量化,计算编码代价 RDCost;
3)选择 RDCost 最小的模式为当前 4×4 块的最优预测 模式。
(4)结合色度编码,统计待编码宏块在Intra_4×4 方式下的率失真代价RDCostI4。
(5)确定最优的帧内预测模式。
比较待编码宏块的RDCostI16 和RDCostI4,选择RDCost小的作为待编码宏块最终的亮度编码方式,该方式对应的预测模式即最优帧内预测模式。
由上述可知:每一个宏块都要按照 2 种方式进行编码,共需要进行 3 328 次预测、144次解码/重构以及 68 次 4×4 哈达马变换,可见其复杂性是相当高的。
快速算法流程
该算法流程如下:
(1)确定 Intra_16 × 16方式下的最优预测模式,步骤同H.264 原算法。
(2)结合色度编码,计算待编码宏块在Intra_16 × 16 方式下的率失真代价RDCostI16。
(3) 计 算 最 优Intra_16 × 16 预 测 模 式 下 待 编 码 宏 块 的SAD,如果 SAD<T1(QP≤20时,T1=500;其余, T1=1000), 则跳到(6)。
(4)确定 Intra_4 × 4 方式下的最优预测模式。
将待编码宏块划分为 16 个 4 ×4 块,分别确定每个 4 × 4 块的最优预测模式:
1)用 9 种预测模式对 4 × 4 块进行预测;
2)分别对每种模式对应的预测残差块进行哈达马变换, 并计算变换后的绝对残差和 SATD;
3)计算SATD的平均值SATDaverage;
4)选择满足SATD ≤SATDaverage 条件的预测模式为候选模式;(减少计算次数,遍历加阈值的方法)
5)分别对每种候选模式对应的预测残差块进行 DCT 变换/量化、反 DCT 变换/反量化,并计算其编码代价RDCost;
6)比较所有候选模式的 RDCost,选择具有最小 RDCost 的预测模式为当前 4×4 块的最优预测模式。
(5)结合色度编码,统计待编码宏块在Intra_4 × 4 方式下的率失真代价RDCostI4。
(6)确定最优的帧内预测模式,步骤同 H.264 原算法。
<<H.264帧内预测和帧间预测的研究>>(硕士论文)
作者:武 敬
研究现状:
目前,帧间预测方面的研究主要集中在快速运动估计、快速参考帧选择和快速模式选择三个方向,特别是在H.264编码中最耗时、最复杂的运动估计方面,投入了大量研究工作,提出了很多快速算法,使得在编码效率几乎没有降低的情况下节省了可观的编码计算时间。这些算法大致可以分为两类:一类是优化运动估计算法,如基于六边形的搜索算法(HBS),增强的预测区域搜索法(EPZS),非对称十字型多六边形混和搜索(UMHexagonS)法以及ARPS-3法。其中UMHexagonS算法在取得相当好的PSNR性能的同时,比以前H.264采用的快速全搜索算法节省了90%-95%的运算量,目前已被JVT采纳。另一类是提前终止运动估计计算,如Libo Yang等人提出的可变块尺寸最优运动检测(VBBMD)算法。
在帧内预测方面,主要从两个方面解决计算复杂度过高的问题:一方面是简化代价函数,另一方面是缩小模式选择的范围。目前提出的快速算法主要有:PAN Feng等利用子块边缘的变化方向从预选模式中选取最有可能的预测方向,根据边缘方向直方图(Edge Direction Histogram)
预先排除一些可能性小的预测模式,从而减小复杂度。
Meng Bo Jun等提出EIP算法,利用代价函数和多阈值的方法提高了4×4子块的编码速度。但是,上述算法存在的主要不足是算法比较复杂,难于实现。因此研究更为有效且易于实现的帧内预测模式选择快速算法具有重要理论意义和应用价值。
视频图像质量的衡量标准
MSE和PSNR从总体上反映重建图像和原始图像的差别,不能反映少数像素点存在的较大灰度差别和较多像素点有较少的灰度差别等情况。对图像中各像素点同样对待,显然不能反映人眼的视觉特性。图像最终是给人观看的,因此合理的图像质量评价方法还应充分考虑主观质量评价。
H.264中的分层结构
如何增强编码视频的网络适应性,扩展视频编码标准的应用范围日益成为人们关注的焦点。
为此,H.264在系统结构设计上引入了全新的分层设计概念,将整个编码系统分成视频编码层(Video Coding Layer,VCL)和网络提取层(Network Abstraction Layer,NAL),如图2.2所示。视频编码层VCL主要负责对数字视频进行高效编解码,提供具有高质量、高压缩比、健壮性、可分级等特性的视频编码码流。
这一部分也是整个H.264视频编码标准的核心部分,但是编码视频比特流对于不同的传输网络和传输协议并不具有一般的适应性。为此,H.264标准在视频编码层的外部定义了网络提取层NAL。NAL主要负责将视频编码层VCL产生的视频编码数据正确、恰当地映射到不同的传输网络。
当VCL产生的编码视频比特流将在某种特定网络中传输时,NAL针对这种网络及其传输协议的特性,对VCL的编码码流进行适合该网络及其传输协议的封装。这样H.264就可以在面向不同的传输网络时,灵活地提供不同的封装方式,增强了网络的适应性。NAL不但使H.264对目前现存的不同网络具有很强的网络友好性,而且使它对未来的网络同样具有很强的适应性。
H.264中的帧间预测
为了更高效的消除视频序列的时域冗余,H.264帧间预测采用了以下新技术:
(1)预测时所用块的大小可变
与仅使用16×16块进行预测相比,使用不同大小和形状的块可以使码率节省15%以上。
(2)精细的预测精度。
运动估计是利用视频图像的时域相关性,产生相应的运动矢量(Motion Vector,MV), 运动补偿是利用运动估计算出的运动矢量,将参考帧图像中的宏块移至水平和垂直方向上的相应位置,即可生成对被压缩图像的预测
H.264支持亮度分量的1/4像素和色度分量的1/8像素的运动估计。如此精细的预测精度较之整数精度可以使码率节省超过20%。
(3)支持多参考帧预测。
采用这一技术,可以改善运动估计的性能,提高H.264解码器的错误恢复能力,较之只使用一个参考帧,使用五个参考帧可以节省5%-10%的码率。
在H.264标准中,运动估计和运动补偿占了全部运算时间的1/2,运动补偿预测编码算法的关键是如何利用当前帧和参考帧估算运动矢量。
H.264帧间模式选择算法流程如下:
(1)对当前宏块的模式16×16,16×8,8×16执行运动估计并计算RDO;
(2)对每个8×8块的模式8×8、8×4、4×8和4×4执行运动估计并计算RDO, 选择RDO最小的模式作为8×8块的最佳模式;
(3)重复执行第二步直到计算完三个8×8块并得出8×8模式的RDO;
(4)为SKIP模式计算预测运动矢量和RDO;
(5)从16×16,16×8,8×16,8×8、8×4、4×8、4×4、SKIP中选择RDO最小的模式作为帧间宏块编码模式。
混合非对称十字多六边形搜索算法
混合非对称十字多六边形搜索算法(UMHexagonS)是目前搜索效果最好的快速搜索算法,已被H.264官方参考软件JM采用。UMHexagonS搜索过程分四步:运动矢量预测、非对称的十字形搜索、不均匀多重六边形搜索、扩展六边形搜索。
图3.10表示当搜索窗为16时的搜索过程,起始点为(0,0)。
(1)按照H.264标准的算法进行运动矢量预测,确定搜索的起始位置。
(2)非对称的十字形搜索。据观察,在自然运动的视频图像序列中,水平方向的运动幅度要大于垂直方向的运动,因此可以通过非对称的十字形搜索进行最佳运动矢量的初步搜索。所谓非对称是指以搜索起点为中心的十字形搜索范围水平方向为垂直方向的2倍,十字形搜索的水平方向长度为搜索窗口宽度,垂直方向为搜索窗口高度的一半,搜索点之间的步长为2。找到当前的最佳匹配点作为下一步的搜索中心。
(3)不均匀多重六边形搜索。这一步分为两步进行。先以当前的搜索中心为中心,在5×5范围内进行全搜索,
如图3.10中Step3.1的圆点所示;然后采用如图3.11 所示的16点六边形搜索模式,进行多重六边形搜索。六边形能覆盖更大的搜索区域,并且在六边形中左右两侧的搜索点数比上下两侧多,也符合自然运动的规律。这种16点六边形搜索由内至外逐层嵌套,搜索出最佳匹配块位置作为下一步搜索的中心。
(4)扩展六边形搜索。上一步的多重六边形搜索得到的运动矢量距搜索中心的距离不同,则运动矢量的精度也不同,当运动矢量位于距搜索中心较远的外部同心六边形区域时,其精度较低,因而需要在此基础上进一步采用一些基于中心的搜索模式进行细化搜索,一般采用六边形模板搜索。先用半径为2的六边形搜索,直至最佳点为六边形的中心,然后采用半径为1的小六边形(小菱形)继续搜索,搜索到最佳匹配位置点位于小六边形的中心为止。与全搜索相比,UMHexagonS能降低90%的整像素运动搜索计算量,而PSNR的下降小于0.1dB,码率基本保持不变,是一种比较好的整像素快速搜索算法。
帧内编码
帧内编码主要应用于以下几种情况:
(1)整个视频序列的第一帧(IDR)。由于编码视频序列的第一帧前没有任何已编码过的帧作为参考帧,因此必须采用帧内编码方式。
(2)I帧。由于规定I帧不参考其它帧的信息独立进行编码,因此I帧必须采用帧内编码方式。
(3)P帧和B帧中的部分宏块。在现有的视频编码标准中(除MPEG-4第二部分),都是先将输入图像在空间上进行宏块级的分割,然后以宏块为基本单位进行编码。对于P帧和B帧中的每一个宏块,都要按照帧间编码和帧内编码两种方式进行编码,利用率失真优化准则计算两种方式的编码代价。对于某一个宏块,如果帧内编码的代价小于帧间编码的代价,则该宏块采用帧内编码方式。造成这种现象主要是由于序列运动过于剧烈,使得参考帧中没有合适的参考块能够很好的H.264帧内预测和帧间预测的研究与待编码宏块匹配,从而导致帧间编码代价过高。另一方面,当待编码帧是场景切换后的第一帧时,由于失去了时间上的相关性,尽管是P帧或B帧,但还是要采用帧内编码方式。
(4)差错恢复:当解码一个采用帧间方式进行编码的宏块时,需要得到当前宏块的参考块信息。如果由于信道的问题导致传输过程中出现差错,在解码端不能得到参考块的信息或得到错误的信息时,当前宏块就不能按照帧间方式正确解码。对于这种受错误影响的区域,可以采用帧内编码方式进行差错恢复。在编码端,通过信源算法或缓冲区算法估计出待编码宏块对应的失真度,以确定该宏块是否采用帧内编码方式。这种差错恢复技术称为帧内更新,也是帧内编码的一个重要应用。
由于视频序列的空间相关性远小于其时间相关性,为了确保获得高压缩比和较好的重构品质,根据视频图像的局部纹理特征和像素值在不同方向上的变化走向,在H.264中总共提供17种帧内预测模式,这样可以对图像局部不同特性的纹理进行较为准确地预测。由图像的统计特性可知,相邻像素随着距离的增大,其相关性呈指数性递减,因此采用不同的块大小在变化起伏不定的场景中效果是不同的。在相对变换较大、包含多个不同对象的场景中,使用相对较小的块对不同的纹理进行更为精细的预测,以提供足够的预测精度。对于4×4块而言,可以对多种方向上的不同纹理特征进行极为准确的预测。而对于平滑的背景区域,使用16×16的块效果会更好,而且一般背景纹理相对平滑,起伏变化小,在标准中只提供了四种预测模式。对于色差块而言,由于人的视觉系统对色度变换的敏感度小于亮度变换的敏感度,因此,对于色度的预测只使用了8×8的块,共四种预测模式。
帧内预测方式
H.264采用了基于空间域进行预测的帧内预测算法,与H.263+高级帧内编码模式中DCT域的帧内预测相比, PSNR值平均提高了4.37dB[7]。
在H.264帧内编码中,每个宏块亮度信号都要完成9种4×4预测模式和4种16×16预测模式,然后通过预测模式选择,得出一种最佳预测模式使得编码性能和效率最高。H.264中模式选择方法有两种:
(1)率失真优化(RDO)方法:对每一种预测模式都要进行预测、整数变换、量化和变长编码,并进行反量化和反变换,再比较各种模式的编码码率和重建图像质量,选择最佳的一种。
(2)计算 Cost 值。 Cost 的计算方法为:
(a)根据一种预测模式构造出4×4的预测块 P ;
(b)计算原始块与预测块P之间的绝对误差和 SAD16
(c)计算Cost16 = SAD16+ 4 Rλ (QP)(4-1)
其中, λ (QP) 是量化因子QP的指数函数,如果当前模式是最有可能预测模式时 R 取值为0, 其他8种情况下R取值为1。SAD表示预测值与图像像素值的差值。
为了更准确地比较每种模式的 Cost 值,H.264还对这些差值进行Hadamard变换,将差值变换到频域求绝对差值和。在这里使用Hadamard变换而不是DCT变换,主要考虑到Hadamard变换比较简单,且比较接近DCT变换。在计算宏块的 Cost 值后,
将 Cost 值最小的模式选为最佳预测模式。对于亮度信号的模式选择,首先在9种4×4预测模式中根据最小 Cost 准则选出一种最佳预测模式,然后在4种16×16预测模式中同样根据最小Cost 准则选出一种最佳预测模式,最后比较一个宏块做一次16×16预测后的 Cost 值 Cost16 和做16次4×4预测后的 Cost 值相加得到的总的 Cost值的大小,选取 Cost 值最小的作为最后宏块的预测模式。如果 Cost 值相同,4×4
预测模式将被优先选取,如果9种4×4预测模式或4种16×16预测模式中出现相同Cost 值时,序号较小的预测模式将被选为最佳预测模式。
实际上,H.264编码器为了得到最佳的模式通常采用RDO技术。对于每一种模式计算出率失真代价 RDCost 。图4.6给出了某一宏块某种模式下 RDCost 的计算过程。采用率失真优化模型可有效地提高编码的率失真效果,但率失真代价的计算需要确定模式对应的比特数以及重建图像与原始图像的失真,这就需要对每一种模式进行预测、变换、量化、编码,得到编码比特数;再对量化后的系数进行反量化、反变换、重建图像,从而得到重建图像与原始图像的失真,因此计算过程十分复杂。而对于不采用率失真优化模型,模式选择只需要进行预测、计算预测残差的SAD或SSD(Sum of Square Difference)。因此,相对而言,采用RDO使得计算复杂度大大增加。
快速帧内预测模式选择算法
减少帧内预测复杂度的方法分为两类:第一类是简化代价函数,第二类是缩小预测模式的选择范围。本文主要讨论第二类方法。对于这类方法,主要是利用当前块及其周围像素的某些特征,预先排除某些可能性很小的预测模式,或提前终止某些可能性小的模式的代价计算,从而降低帧内预测的复杂度。由于H.264帧内编码是基于帧内冗余性的,空间相关性较大。因此,基于空间域的快速算法在数量上占有绝对优势。
帧、场编码的个人理解
一、概述
原始视频帧(最原始的视频数据)根据编码的需要,以不同的方式进行扫描产生两种视频帧:连续或隔行视频帧,隔行视频帧包括顶场和底场,连续(遂行)扫描的视频帧与隔行扫描视频帧有着不同的特性和编码特征,产生了所谓的帧编码和场编码。一般情况下,遂行帧进行帧编码,隔行帧可在帧编码和场编码间选取。
在帧编码中,参考为帧图像,采用帧运动补偿,两个场是联合编码;在场编码中,参考为场图像,两个场是分别编码,采用场运动补偿。
二、视频序列帧、场编码方式
1.固定帧编码(全帧)----视频序列的全部帧始终采用帧编码方式。
2.固定场编码(全场)
视频序列中帧被分成两个场独立编码。编码规则:
(1).I帧可编码成两个I场或一个I场和一个P场,即II、IP.
(2).P帧可编码成两个P场或一个P场和一个B场,即PP、PB.
(3).B帧可编码成两个B场,即BB.
H.264采用的MBAFF方案:
在图像中运动比较大的地方采用场编码,运动比较小的地方采用帧编码,编码顺序例子如下:
第四章 视频编码基础
1. 压缩码流
- 语法:码流中各个元素的位置关系
- 01001001…
- 图像编码类型(01),宏块类型(00),编码系数1001等
- 语义:每个语法元素所表达的意义。
- 例如:图像编码类型
2. 编码层次
- 序列(Sequence)
- 图像组(Group of Pictures,GOP)
- 图像(Picture)
- 条带(Slice)
- 宏块(Macroblock,MB)
- 块(Block)
3. 码流结构
3. PB帧编码
4. 序列编码对象
(1)IBBP序列
- 序列:一段连续编码的并具有相同参数的视频图像。
- 序列起始码:专有的一段比特串,标识一个序列的压缩数据的开始
- MPEG-2的序列起始码为十六进制数000001(B3)。
- 序列头:记录序列信息
- 档次(Profile),级别(Level),宽度,高度,是否是逐行序列,帧率等。
- 序列结束码:专有的一段比特串,标识该序列的压缩数据的结束
- MPEG-2的序列结束码为十六进制数000001(B7)。
5. 图像组编码对象
6. 图像编码结构
- 图像:
- 图像起始码:专有的一段比特串,标识一个图像的压缩数据的开始
- MPEG-2的图像起始码为十六进制数000001(00)。
- 图像头:记录图像信息
- 图像编码类型,图像距离,图像编码结构,图像是否为逐行扫描。
7. 图像分块编码
8. 条带编码结构
- 条带:多个宏块的组合。
- 条带起始码:专有的一段比特串,标识一个条带的压缩数据的开始
- MPEG-2的条带起始码为十六进制数000001(0~AF)。
- 条带头:记录当前图像的相关信息
- 条带位置,条带量化参数,宏块编码技术标识等。
9. 条带编码对象
10. 宏块编码结构
- 宏块:16x16的像素块(对亮度而言)。
- 宏块内容:宏块编码类型,编码模式,参考帧索引,运动矢量信息,宏块编码系数等。
11. 宏块编码对象
12. 块编码结构
- 8x8或4x4块的变换量化系数的熵编码数据。
- CBP (Coded Block Patten):用来指示块的变换量化系数是否全为零。
- 对于YUV(4:2:0)编码,CBP通常6比特长,每个比特对应一个块,当某一块的变换量化系数全为零时,其对应比特位值为0,否则为1。
- 每个块的变换量化系数的最后用一个EOB (End of Block)符号来标识。
13. 视频编解码关键技术
- 预测:通过帧内预测和帧间预测降低视频图像的空间冗余和时间冗余。
- 变换:通过从时域到频域的变换,去除相邻数据之间的相关性,即去除空间冗余。
- 量化:通过用更粗糙的数据表示精细的数据来降低编码的数据量,或者通过去除人眼不敏感的信息来降低编码数据量。
- 扫描:将二维变换量化数据重新组织成一维的数据序列。
- 熵编码:根据待编码数据的概率特性减少编码冗余。
14. 预测
- 空间预测:利用图像空间相邻像素的相关性来预测的方法。
- 帧内预测技术:利用当前编码块周围已经重构出来的像素预测当前块
- Intra图像编码(I帧)
- 时间预测:利用时间上相邻图像的相关性来预测的方法。
- 帧间预测:运动估计(Motion Estimation,ME),运动补偿(Motion Compensation,MC)
- Inter图像编码:前向预测编码图像(P帧),双向预测编码图像(B帧)
15. 帧内预测
- I帧图像的每个宏块都采用帧内(Intra)预测编码模式。
- 宏块分成8x8或者4x4块,对每个块采用帧内预测编码,称作Intra8x8或者Intra4x4。
- 帧内预测有多个预测方向:水平,垂直,左下,右上。
- 帧内预测还有直流(DC)预测。
- 色度块预测还有平面预测。
16. 帧间预测
- 块基运动估计:为待预测块在参考帧上找到最佳的预测块,并记录预测块在参考帧上的相对位置。
- 运动矢量(MV):参考帧上的预测块与当前帧上的的待预测块的相对位置。
- MV有两个分量:(x,y)
- 分像素运动估计
- 最佳的预测块不在整像素位置,而在分像素位置;
- 1/2,1/4,1/8像素插值得到分像素值。
- 帧间预测流程:
- 运动补偿:给定MV和参考帧,为待解码块从参考帧上获取预测块。
- 运动矢量编码
- MV预测:用当前块的周围可得到邻块的运动矢量来预测当前块的运动矢量
- 运动矢量差(MV difference,MVD):实际运动矢量与预测运动矢量的差,即:
- 运动矢量差采用变长编码。
17. 预测残差
18. 变换编码
- 变换编码:通过变换将空域信号转换为频域信号来去除空间信号的冗余信息,减少编码数据。
- 二维离散余弦变换
- 4x4变换,8x8变换
- 二维离散余弦变换
例:
- 变换系数:直流(DC)系数,交流(AC)系数
19. 量化
- 量化原理:将含有大量的数据集合映射到含有少量的数据集合中
- 一般情况下量化后高频部分包含大量的零系数
- 量化对主观质量的影响
20. 扫描
- 扫描:将二维数据转换为一维的数据序列。
21. 熵编码
- 熵编码:根据符号出现的概率,对经常出现的符号分配较短的码字,对不常出现的符号分配较长的码字。
- Level-Run编码:用数据中非零值和其前面非零值之间出现零值的个数重新描述量化系数序列为(Level,Run)二元组序列
- 变长编码
- 将Level-Run编码后的(level,run)变长编码成最终的比特串。
22. 码率控制
- 受到缓冲区,带宽的限制,编码码率不能无限制的增长,因此需要通过码率控制来将编码码流控制在目标码率范围内。
- 一般通过调整量化参数的手段控制码率
- 帧级控制
- 条带级控制
- 宏块级控制
- 码率控制考虑的问题
- 防止码流有较大的波动,导致缓冲区发生溢出,
- 同时保持缓冲区尽可能的充满,让图像质量尽可能的好而且稳定
- CBR(Constant Bit Rate)
- 比特率稳定,但图像质量变化大
- VBR(Variable Bit Rate)
- 比特率波动大,但图像质量稳定
- 码率控制算法
- 码率分配
- 码率控制
- 码率控制属于非标准技术
- 编码端有,解码端没有
第5章 预测
1. 预测技术
- 目的:去除空间冗余和时间冗余。
- 视频存在大量的空间冗余和时间冗余
- 空间冗余:用帧内预测编码去除
- 基于块的帧内预测
- 时间冗余:用帧间预测编码去除
- 基于块匹配(Block Matching)的帧间预测
- 预测后得到去除大部分空间或时间冗余的残差
2. 空间冗余
- 图像空间相邻像素具有很强的相关性。
- 帧内预测技术去除空间冗余
3. 亮度预测模式
4. 色度预测模式
5. 时间冗余
- 视频图像在时间上有较强的相关性,即存在时间冗余
- 去除时间冗余的编码技术
- 运动估计(Motion Estimation,ME)
- 为待编码块搜索最相似的预测块
- 记录运动矢量(Motion Vector,MV)
- 记录预测残差:
- 运动补偿(Motion Compensation,MC)
- 根据运动矢量获取预测块
- 根据预测残差计算重构块:
6. 运动模型
(1)平移
7. 匹配准则
8. 匹配准则简化
- 简化技术方法
- 分别计算当前块和预测块的象素值和
- 根据简化形式,比较当前块和预测块
- 如果用简化准则对预测块和当前块比较的结果比以前最好的结果差,可以确定预测效果不好,不必对预测块再进行比较。
9. 运动估计
- 去除视频图像的时间冗余
- 运动估计在搜索范围内为当前块寻找匹配最好的预测块
- 全搜索方式的运动估计计算复杂度高
10. 全搜索复杂度分析
- 图像大小:MxM
- 预测块大小:NxN
- 搜索范围:(-R,R)
- 每个搜索点象素比较个数:N2
- 搜索点个数(2R+1)2
- 在搜索范围内的象素比较个数总和N2(2R+1)2
- 一帧图像所有块的全搜索象素比较个数总和N2(2R+1)2(M/N)2=(2R+1)2M2
- 例:M=512,N=4,R=8,帧率:30帧/秒
(2R+1)2M2
=172X5122
= 75759616次/帧
= 75759616x30次/秒
=2272788480次/秒
采用SSD匹配准则:每次象素比较需1个减法,1个乘法,1个加法,则上述全搜索计算每秒需要2272788480x2次加减法和2272788480次乘法操作。
11. 快速运动估计
- 在保持预测精度的同时减少运动估计的搜索次数。
- 三步搜索(Three Step Search,TSS)
- 二维Log搜索(2D Logarithmic Search,2DLOG)
- 正交搜索(Orthogonal Search Algorithm,OSA)
- 十字搜索(Cross Search Algorithm,CSA)
- 新三步搜索(New Three Step Search,NTSS)
- 四步搜索(Four Step Search,FSS)
- 共轭方向搜索(Conjugate Direction Search,CDS)
- 梯度下降搜索(Gradient Descent Search,GDS)
- 层次块搜索(Hierarchical Block Matching Algorithm,HBMA)
12. 三步搜索
- 由粗到精搜索最优点,初始步长为R/2.
- 第一步:检查起始点和其周围步长为R/2的8个点,将最优点作为第二步的起始点;
- 第二步:以新的起始点为中心检查其周围步长为R/4的8个点,找到最优点作为第三步的起始点;
- 第三步:以新的起始点为中心检查其周围步长为R/8的8个点,找到最优点,如果R/8=1则搜索终止,最优点位置的预测块作为最优的预测块,否则重复该过程直到R/n2=1;
- 三步搜索方法检查点的个数为1+8log2(d+1),当d=8时,检查点个数为9+8+8=25
13. 二维Log搜索
- 每一步采用十字搜索模式
- 如果每一步的最优点为中心点或者搜索窗的边界点,搜索步长减半,否则搜索步长不变
- 当搜索步长为1时,中心点周围的8个点都要检查
- 两个搜索路径一个需要5+3+3+8=19,另外一个需要5+3+2+3+2+8=23
14. 正交搜索
- 起始搜索步长R/2,从起始点开始水平搜索三个点,得到最优点并沿着最优点垂直方向搜索相邻的两个点,得到最优点,以搜索步长为R/4再以同样的方式先水平再垂直搜索,当步长为1时停止搜索
- 搜索方法检查点的个数为1+4log2(d+1),当d=8时,检查点个数为3+2+2+2+2+2=13。
15. 十字搜索
- 起始搜索步长R/2,从起始点开始以"X"形十字搜索,当搜索步长降为1时,如果上一步的最优点为中心点,左上点或右下点,则这一步搜索以"+"形状十字搜索,然后结束搜索,否则还是以"X"形十字搜索,然后结束搜索。
- 十字搜索方法检查点的个数为1+4log22d,当d=8时,检查点个数为5+4+4+4=17
16. 新三步搜索
- 与三步搜索方法不同的是,考虑到运动矢量高的中心分布特点,新三步搜索方法,除了围绕起始点为中心搜索步长为R/2的8个点之外,在起始点周围增加了步长为1的8个搜索点,如果最优点为步长为1的8个搜索点之一,则在最优点邻近的三个点中搜索最优点,然后结束搜索,否则,和三步搜索方法过程一样
- 其中一个搜索路径需要检查点个数为17+3=20,另一个需要17+8+8=33。
17. 块梯度下降搜索
- 该方法以起始点为中心搜索8个步长为1的相邻点,确定最优点,再以最优点为中心搜索8个步长为1的相邻点,如此循环下去,不限制搜索步骤,但当搜索得到的最优点为中心点或者到搜索窗的边界,搜索终止。
18. 层次块搜索
- 对编码图像和参考图像下采样,分别得到编码图像和参考图像的下采样图像,未经采样处理的编码图像和参考图像属于第0层,一次下采样的编码图像和参考图像属于第1层,对第1层图像再进行下采样得到的编码图像和参考图像属于第2层,依次重复上述过程,得到第n层下采样的编码图像和参考图像。
- 然后在n层下采样参考图像的搜索范围中找到与下采样编码图像块最佳匹配块的MV,该MV作为n-1层的运动估计搜索范围的中心点,依次重复上述过程,直到n=0为止,此时得到的最佳匹配块就是编码图像的预测块,其对应的MV为最终的最优MV。
19. 搜索算法复杂度比较
20 . 分像素运动估计与运动补偿
- 时域运动位置更可能在整象素之间,即分像素上。
- 利用相邻的整象素可以估计出分象素的值
- 常用线性或双线性插值得到分象素的值。
- 分象素运动估计有更高的预测精度,但复杂度也更高,
- 1/2分象素运动估计,图像存储空间增加4倍,运动矢量需要放大2倍,1/4分象素运动估计,图像存储空间增加16倍,运动矢量需要放大4倍,计算复杂度也成倍增加。
21. 分像素插值
22. 多参考帧预测
- 有更多的候选图像,搜索更精确的预测块
- 需要更多的参考图像存储空间
- 码流需要标识参考帧索引的语法元素
23. 图像分块编码
- 视频内容的运动非常复杂,图像分块编码可以更好的提高运动预测精度,提高压缩效率。
- 要在编码块大小和附信息(MV,Mode)编码比特数之间权衡,小的编码块大小会有更好的预测但有更多的附信息比特数。
23. 双向预测编码
24. B帧有更好的编码效率
- B帧有更好的编码效率
- 新出现的对象参考将来的帧有更好的预测效果
- 前后两个预测的平均值可以减少预测方差
25. 全局运动估计
- 基于全局仿射运动模型
- 预测精度不如基于块的运动估计
- MV数目少,适合简单运动场景的运动估计
- JM(264)基础知识整理
- JM
- jm
- java基础知识(整理)
- 四. 常见H.264视频编解码器(X264和JM)及参考软件JM的下载与编解码
- [JM] 如何结合标准看JM代码(JM86)
- H.264+JM学习笔记
- H.264+JM学习笔记
- JM代码分析(一)
- JM代码分析(一)
- JM代码分析(二)
- C#基础知识整理:基础知识(2) 类
- C#基础知识整理:基础知识(4) 继承
- C#基础知识整理:基础知识(8) 接口
- C#基础知识整理:基础知识(10) 静态
- C#基础知识整理:基础知识(13) 异常
- C#基础知识整理:基础知识(14) 数组
- C#基础知识整理:基础知识(14) 数组
- 服务化实战之 dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型
- phpcmsv9禁止提交信息到官网
- 推荐系统测评指标
- MySQL编译安装 以及 单主机多MySQL实例的搭建
- CodeForces 847CSum of Nestings
- JM(264)基础知识整理
- 利用NVM搭建Nodejs的开发环境
- Spring boot 集成MyBatis(1)
- deeplearning4j安装与学习
- 【剑指offer】连续子数组最大和
- HTML web网页键盘无障碍访问(张鑫旭大神)
- hdfs复制数据到本地服务器+本地服务器复制数据到远程服务器+重命名文件
- 天池商铺定位数据挖掘竞赛思路总结
- Hibernate 懒加载