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算出當前幀QP
- 把當前幀QP與前面幀的QP進行對比,做平滑處理
為了算出當前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}$則是用當前幀未編碼的Basic Unit的目標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為單位。
還有就是平滑(略)。