h.264码率控制
h.264的码流传输是基于目前有限的网络带宽来进行的,以目前的压缩效率来说,运动不算剧烈、细节不多的影像,在720p的情况下,1000kbps压缩损耗较少(psnr较大),能达到比较好的观赏效果,1080p则需要2000kbps。当然,随着图像运动剧烈程度加大,细节增多的情况,则需要更大的bps来保证图像效果。
另外由于图像序列中,并非所有的图像都有相同的细节,因此应该在细节多的图像上分配更多的bit,而细节少的图像则分配更少的bit。同一道理,在一张图像上,可能某些位置的细节较多,那就应该在那里分配跟多的bit,否则分配更少的bit。
h.264的编码码率控制就是考虑到以上的这些情况,制定了码率控制标准。以下以JVT-H017r3为例,分析h.264码率控制模块。
码率控制三个阶段
如前面所说,h.264码率控制可以分为三个阶段:
- GOP级码率控制
- Picture级码率控制
- Basic Unit级码率控制
GOP级的码率控制
h.264的码流控制是建立在存在虚拟参考解码器的基础上的,即希望通过码率控制做到:编码端编码出来的码流,在经过设定好的bps传输后,解码端解码播放时是同步的(不延迟),下面的outflow可以看做是传输到解码端。
可以通过上图加以理解,途中为某个GOP在某一时刻的编码状态,Not coded 为未编码图像,coded为已编码图像,图像编码后会以Bit Stream(码流)的形式输出到Virtual Buffer中,这里的Virtual Buffer 并不一定实际存在于实际编码器中,而是算法虚拟出来的缓存区,主要是维护码流的输入与输出,在缓冲区中的码流会以某一速率进行输出(outflow)。
1. Virtual Buffer
对于Virtual Buffer的大小,是可计算的
$\begin{align*}
&V_i(1) = \left\{\begin{matrix}
0 & i=1\\
V_{i-1}(N_{i-1}) & other
\end{matrix}\right.\\
& V_i(j) = V_i(j-1) + b_i(j-1) - \frac{R_i(j-1)}{f} \qquad j=2,3,...,N_i
\end{align*}$
$i$表示第$i$个GOP,$j$表示第$i$个GOP的第$j$幅图像,
- 如果是视频序列的第一帧,那么以编码数为$0$,缓存区大小肯定为$0$,
- 如果是其他GOP的第一帧,那么缓存区大小则为上一个GOP剩下来的大小。
- 在其他情况下,缓存区大小为上一帧的缓存区大小加上上一帧编码后码流大小,减去码流流出的大小。
2. Remain Picture Bit
对于当前GOP未编码的图像的bit大小,是未知的,因此需要进行预测
$B_i(j) = \left\{\begin{matrix}
\frac{R_i(j)}{f} \times N_i - V_i(j) & j=1\\
B_i(j-1) + \frac{R_i(j)-R_i(j-1)}{f} \times (N_i-j+1) - b_i(j-1) & j=2,3,...,N_i
\end{matrix}\right.\\$
在设定初始值的时候,请注意$-Vi(j)$,这么做其实是为了清空缓存区。$B_i(j) + V_i(j) = \frac{R_i(j)}{f} \times N_i$,设定左右两边相等,也就是说希望在码流流出为$N_i$帧的时间里,能把当前GOP中所有的bit以及原本Virtual Buffer中存放的bit一同清空,减少接受端的延迟。
在其他情况下,预测值B收到上一帧的预测值影像,也收到带宽的变化影响,最后当然要减去上一帧编码所用的bit数。
在码率没有变化的情况下,会被简化成:
$B_i(j) = B_i(j-1) - b_i(j-1)$
3. 设定I帧QP
对于一个GOP,首当其冲是要设定好它第一帧(I、IDR)的QP,如果是视频序列的第一帧,也就是第一个GOP的第一帧:
$QP_1(1)=\left\{\begin{matrix}
40 & bpp \leqslant l_1\\
30 & \qquad l_1 < bpp \leqslant l_2\\
20 & \qquad l_2 < bpp \leqslant l_3\\
10 & bpp > l_3
\end{matrix}\right.$
$bpp = \frac{R_1(1)}{f \times N_{pixel}}$
$N_{pixel}$是一张图像的像素点个数。
对于QCIF/CIF,$l_1=0.15, l_2=0.45, l_3=0.9$
对于大于CIF的图像,$l_1=0.6, l_2=1.4, l_3=2.4$
而对于其他GOP,有
$QP_i(1)=max\{ QP_{i-1}(1) - 2,\ min\{ QP_{i-1}(1)+2, \ \frac{SumPQP(i-1)}{N_p(i-1)} - min\{ 2, \ \frac{N_{i-1}}{15} \}\} \}$
平滑处理:
$QP_i(1) = QP_i(1)-1 \quad if \quad QP_i(1) > QP_{i-1}(N_{i-1}-L)-2$
Picture级码率控制
1. 设定B帧QP
对于B帧,都是以B帧两端的参考帧的QP来计算当前B帧QP值。分两种情况,假设两个参考帧间的B帧数为$L$。
当$L = 1$,
$QP_i(j+1)=\left\{\begin{matrix}
\frac{QP_i(j)+QP_i(j+2)+2}{2} & if \ QP_i(j) \neq QP_i(j+2) \\
QP_i(j)+2 & Otherwise
\end{matrix}\right.$
当$L > 1$,
$QP_i(j+k)=QP_i(j)+\alpha +max\{ min\{ \frac{QP_i(j+L+1)-QP_i(j)}{L-1}, 2\times(k-1)\}, -2\times(k-1) \}$
$\alpha = \left\{\begin{matrix}
-3 & QP_i(j+L+1)-QP_i(j) \leqslant & -2\times L-3\\
-2 & QP_i(j+L+1)-QP_i(j) \leqslant & -2\times L-2\\
-1 & QP_i(j+L+1)-QP_i(j) \leqslant & -2\times L-1\\
0 & QP_i(j+L+1)-QP_i(j) \leqslant & -2\times L\\
1 & QP_i(j+L+1)-QP_i(j) \leqslant & -2\times L+1\\
2 & Otherwise
\end{matrix}\right.$
2. 设定P帧QP
P帧码率控制主要分为三步:
- 算出当前P帧的目标bit
- 通过目标bit算出当前帧PQ
- 把当前帧PQ与前面帧的PQ进行对比,做平滑处理
为了算出当前P帧的目标bit,需要的参数有三个:Virtual Buffer,Remain Picture Bit,Target Buffer Level。其中前两个参数在前面的GOP级的码率控制中已经获得,下面来引入讨论一个新的概念,目标缓存级别(Target Buffer Level)。
这个级别(Level)用来修正待编码图像对清空缓存区的贡献,基本思想是清空缓存区的工作应该更多由非参考图像来承担,由于B帧的QP普遍会比两端的P帧大,也就是相对来说会占用更小的缓存区域,那么P帧就可以降低QP来提高图像编码质量,那么P帧编码为码流后占用的缓存区会更大。Target Buffer Level 代表的就是P帧在缓存区的占用情况,当有B帧时的Target Buffer Level 会比没有B帧时的大。
计算方式如下:
$S_i(2)=V_i(2)$
$S_i(j+1)=S_i(j)-\frac{S_i(2)}{N_p(i)-1}+\frac{\bar{W}_{p,i}(j)\times(L+1)\times R_i(j)}{f\times (\bar{W}_{p,i}(j) + \bar{W}_{b,i}(j) \times L)} - \frac{R_i(j)}{f}$
$\begin{align*}
\bar{W}_{p,i}(j) &= \frac{W_{p,i}(j)}{8} + \frac{7 \times \bar{W}_{p,i}(j-1)}{8} \\
\bar{W}_{b,i}(j) &= \frac{W_{b,i}(j)}{8} + \frac{7 \times \bar{W}_{b,i}(j-1)}{8} \\
W_{p,i}(j) &= b_i(j) \times QP_{p,i}(j) \\
W_{b,i}(j) &= \frac{b_i(j) \times QP_{b,i}(j)}{1.3636}
\end{align*}$
$W_p$是P帧的权重,$W_b$是B帧的权重。
当没有B帧时,可以简化为:
$S_i(j+1)=S_i(j)-\frac{S_i(2)}{N_p(i)-1}$
这表明Target Buffer Level会越来越小,在当前GOP末尾会趋向于$0$。
最后结合Virtual buffer,Remain Picture bit,Target Buffer Level三者求出当前P帧的目标bit。
$\begin{align*}
\tilde{T}_i(j) &= \frac{R_i(j)}{f} + \gamma \times (S_i(j) - V_i(j)) \\
\hat{T}_i(j) &= \frac{W_{p,i}(j-1) \times B_i(j)}{W_{p,i}(j-1) \times N_{p,\gamma} + W_{b,i}(j-1) \times N_{b,\gamma}} \\
T_i(j) &= \beta \times \hat{T}_i(j) + (1-\beta) \times \tilde{T}_i(j)
\end{align*}$
当然还有上下界判断(略)。
得到目标bit后就可以求当前P帧的量化步长(求解下面一元二次方程),然后通过量化步长得到量化参数,
$T_i(j) = c_1 \times \frac{\tilde{\sigma}_i(j)}{Q_{step,i}(j)} + c_2 \times \frac{\tilde{\sigma}_i(j)}{Q_{step,i}^2(j)} - m_{h,i}(j)$
$\tilde{\sigma}_i(j) = a_1 \times \sigma_i(j-1-L) + a_2$
其中,$\sigma_i(j-l-L)$为上一P帧的复杂度,$\tilde{\sigma}_i(j)$为当前P帧复杂度的预测值,$m_{h,i}(j)$则是当前P帧的运动向量以及头部大小。
最后也少不了对比上一P帧进行平滑QP的处理(略)。
Basic Unit级码率控制
这个其实跟“设定P帧QP”有同样的三个步骤,只是把Picture分开了成Basic Unit然后再一一计算,对当前Basic Unit进行码率控制、编码,然后轮到下一个Basic Unit。
首先需要求当前basic unit的目标bit数,
$\tilde{b_l} = T_{r} \times \frac{\tilde{\sigma}_{l,i}^2(l)}{\displaystyle{\sum_{k=l}^{N_{unit}}\tilde{\sigma}_{l,i}^2(k)}}$
其中,$\tilde{\sigma}_i(j)$为当前Basic Unit复杂度的预测值,该预测值是通过上一P帧的对应Basic Unit位置来进行预测的,预测方法同上方P帧,采用线性预测,$T_{r}$则是用当前帧的目标bit数$T_i(j)$。
第二步要预测当前P帧的Basic Unit平均的头部大小(包括mv等)。
$\begin{align*}
\tilde{m}_{hdr,l} &= \tilde{m}_{hdr,l-1} \times (1-\frac{1}{l}) + \frac{\hat{m}_{hdr,l}}{l} \\
m_{hdr,l} &= \tilde{m}_{hdr,l} \times \frac{l}{N_{unit}} + m_{hdr,1} \times (1 - \frac{l}{N_{unit}});1 \leqslant l \leqslant N_{unit}
\end{align*}$
其中,$\tilde{m}_{hdr,l}$是序号为$l$的Basic Unit的header bit初步预测值,$\hat{m}_{hdr,l}$是当前Basic Unit已经产生的header bit数,$m_{hdr,1}$是上一个P帧的Basic Unit的header bit的平均值。那么上面的式子可以这样理解:等式1是用前一个Basic Unit的header bit初步预测值与当前Basic Unit已产生的header bit来预测当前Basic Unit的初步预测值;等式2是用上一张P帧的Basic Unit的header bit平均值对当前Basic Unit初步预测值进行修正。
然后用目标bit减去header bit得到预测的纹理bit数。
$\hat{b}_l = \tilde{b}_l - m_{hdr,l}$
最后就是求量化参数了,这个跟上面Picture级的一样,只是把复杂度换成了以Basic Unit为单位。
还有就是平滑(略)。
h.264码率控制的更多相关文章
- H.264 码率设置
一.什么是视频码率 视频码率是视频数据(包含视频色彩量.亮度量.像素量)每秒输出的位数.一般用的单位是kbps. 二.设置视频码率的必要性 在网络视频应用中,视频质量和网络带宽占用是相矛盾的.通常情况 ...
- 最近调试HEVC中码率控制, 发现HM里面一个重大bug
最近调试HEVC中码率控制, 发现里面一个重大bug! 码率控制中有这么一个函数: Int TEncRCGOP::xEstGOPTargetBits( TEncRCSeq* encRCSeq, Int ...
- H.264编码profile & level控制
背景知识 先科普一下profile&level.(这里讨论最常用的H264) H.264有四种画质级别,分别是baseline, extended, main, high: 1.Baseli ...
- H.264的优势和主要特点
H.264,同时也是MPEG-4第十部分,是由ITU-T视频编码专家组(VCEG)和ISO/IEC动态图像专家组(MPEG)联合组成的联合视频组(JVT,Joint Video Team)提出的高度压 ...
- 2016-06-06:X264码率控制
H.264与x264 H264是一个视频压缩编码标准.https://zh.wikipedia.org/wiki/H.264/MPEG-4_AVC X264实现H264视频压缩标准的开源项目.http ...
- h.264 去块滤波
块效应及其产生原因 我们在观看视频的时候,在运动剧烈的场景常能观察到图像出现小方块,小方块在边界处呈现不连续的效果(如下图),这种现象被称为块效应(blocking artifact). 首先我们需要 ...
- H.264视频在android手机端的解码与播放(转)
随着无线网络和智能手机的发展,智能手机与人们日常生活联系越来越紧密,娱乐.商务应用.金融应用.交通出行各种功能的软件大批涌现,使得人们的生活丰富多彩.快捷便利,也让它成为人们生活中不可取代的一部分.其 ...
- 音视频编解码技术(一):MPEG-4/H.264 AVC 编解码标准
一.H264 概述 H.264,通常也被称之为H.264/AVC(或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC) 1. H.264视频编解码的意义 H.264的出现就是为了创 ...
- h.264并行熵解码
在前面讨论并行解码的章节中,我们专注于讨论解码的宏块重建部分,甚至把宏块重建描述成宏块解码,这是因为在解码工作中,宏块重建确实占了相当大的比重,不过解码还包含其它的部分,按照解码流程可粗略分为: 读取 ...
随机推荐
- Demo_CS(移动,切换枪支,发射子弹)
using UnityEngine; using System.Collections; public class Gun : MonoBehaviour { private Animator ani ...
- 卸载Symantec Endpoint Protection, 无需password的卸载方法
近期一次偶然的机会, 被人装了个Symantec在电脑上, 搞得各种报警, 验证, 烦死. 然后就自然而然的想卸载掉这个该死的杀毒软件, 没想到这个杀毒软件竟然还是个流氓杀毒软件, 卸载还须要pass ...
- 排序(5)---------高速排序(C语言实现)
继shell发明了shell排序过后呢,各位计算机界的大牛们又開始不爽了,为什么他能发明.我就不能发明呢.于是又有个哥们蹦出来了.哎...那么多排序,就木有一个排序是中国人发明的.顺便吐槽一下,一百年 ...
- cocos2d-x 背景音乐播放
Code // on "init" you need to initialize your instance bool HelloWorld::init() { bool ...
- Swift的闭包(二):捕获值
闭包可以从定义它的上下文中捕获常量和变量. 在Swift中,捕获值最简单的例子是嵌套函数,举个例子: func makeIncrementer(forIncrement amount: Int) -& ...
- [转] doxygen使用总结
doxygen [功能] 为许多种语言编写的程序生成文档的工具. [举例] *生成一个模板配置文件,模板文件中有详细的注释: $doxgen -g test 这样,会生成一个test文件,1500多行 ...
- HDU5308-脑补-对拍
先贴上对拍的结果: 感想:可以明显发现下标相差1的关系,所以对付这种需要大量脑补的水题,真的蛋疼,以前没用过对拍,只知道有这么回事,调程序什么的都是手算,人工模拟(经常大脑发热,严重缺氧不足),所以试 ...
- Cacti以MB为单位监控流量
Cacti自带的流量监控阀值模板为“Interface – Traffic”,只能监控bytes,在添加阀值之后,报警的流量信息以bytes为单位,查看很不友好,可以通过以下方法将btyes转换成MB ...
- OC - 16.大文件下载
大文件下载注意事项 若不对下载的文件进行转存,会造成内存消耗急剧升高,甚至耗尽内存资源,造成程序终止. 在文件下载过程中通常会出现中途停止的状况,若不做处理,就要重新开始下载,浪费流量. 大文件下载的 ...
- Swift - 14 - 字符串的基础操作
//: Playground - noun: a place where people can play import UIKit // 拼接 var str = "Hello, playg ...