CAVLC
在H.264標準中,CAVLC(Context-based Adaptive Variable Length Coding)被用于亮度和色度殘差數(shù)據(jù)編碼。在標準的碼流結(jié)構(gòu)中,CAVLC編碼方式描述為ce(v)。如果在編碼時采用CAVLC,那么盡管在DCT時是以8x8塊為單位進行的,在進行CAVLC時也會強制采用4x4塊為單位進行編碼(請參考h.264語法結(jié)構(gòu)分析中的redisual_luma部分)。在進行熵編碼之前,需要把4x4塊的矩陣中的元素按照一定順序重新排列成大小為16的序列,這部分工作請參考h.264 scanning process for transform coefficients,熵編碼就是以這些序列為基礎進行編碼的。
?
CAVLC可分為五個部分
- 編碼非零系數(shù)的數(shù)目(TotalCoeffs)、拖尾系數(shù)(TrailingOnes)的數(shù)目
- 對每個拖尾系數(shù)(TrailingOnes)的符號進行編碼
- 對除了拖尾系數(shù)之外的非零系數(shù)(level)進行編碼
- 對最后一個非零系數(shù)前面零的數(shù)目(TotalZeros)進行編碼
- 對每個非零系數(shù)前面連續(xù)的零的個數(shù)(RunBefore)進行編碼
?
?
一. 編碼TotalCoeffs,TrailingOnes
TotalCoeffs:非零系數(shù)數(shù)目。變換系數(shù)level中所有不為0的level的數(shù)目。
TrailingOnes:拖尾系數(shù)的數(shù)目。指的是矩陣重排列后,序列末尾連續(xù)出現(xiàn)的$\pm 1$的個數(shù)(中間可以間隔任意多個0)。如果$\pm 1$的個數(shù)大于3個,則只有最后三個$\pm 1$會被視為拖尾系數(shù),其余的被視為普通的非零系數(shù)。
我們用一個例子來解釋TotalCoeffs以及TrailingOnes
在上述例子中,共有5個非零系數(shù),因此TotalCoeffs為5;其中有四個非零系數(shù)都是$\pm 1$,只有最后三個被視為拖尾系數(shù),因此TrailingOnes為3。
?
TotalCoeffs與TrailingOnes是一起進行編碼的,編碼通過查表的方式進行,碼表請參考標準中的表9-5。
用來編碼TotalCoeffs與TrailingOnes的碼表,需要根據(jù)變量nC的值進行選擇。除了色度的直流系數(shù)(Chroma DC)之外,其它系數(shù)類型的nC值是根據(jù)當前塊左邊4x4塊的非零系數(shù)數(shù)目(nA)和當前塊上邊4x4塊的非零系數(shù)數(shù)目(nB)求得。nC的求值過程見下表。其中“X”表示與當前塊同屬于一個slice并可用。
| 上邊的塊(nB) | 左邊的塊(nA) | nC |
| X | X | (nA+nB)/2 |
| X | - | nB |
| - | X | nA |
| - | - | 0 |
另外,如果輸入的系數(shù)是色度的直流系數(shù)(而且視頻格式為4:2:0),nC=-1;如果輸入的系數(shù)是色度的直流系數(shù)(而且視頻格式為4:2:2),nC=-2。
?
原理上說,nC代表了當前塊的相鄰塊非零系數(shù)的情況,由于塊與塊之間的相關性,當我們知道了相鄰塊的非零系數(shù)的情況,就有很大概率知道了當前塊非零系數(shù)的情況。有了概率,h.264標準在這里引入了huffman編碼,不過huffman的編碼過程不用我們自己處理,h.264標準已經(jīng)根據(jù)實驗得到的數(shù)據(jù)組合成了碼表9-5。
?
?
二. 對TrailingOnes的符號進行編碼
在第一步時,對于TrailingOnes,我們只編碼了它的個數(shù)。在這一步,我們對TrailingOnes的符號進行編碼。對每個拖尾系數(shù)需要用一個bit表示它的符號。標準規(guī)定用0表示“+”,用1表示“-”。編碼順序是按照掃描逆序進行,也就是從高頻數(shù)據(jù)開始。
?
?
三. 對除了拖尾系數(shù)之外的非零level進行編碼
除了拖尾系數(shù)之外的非零level可能會有多個,編碼順序與上述TrailingOnes的一樣,也是逆序進行。
?
對于每個level,編碼結(jié)果包括兩部分:前綴(level_prefix)和后綴(level_suffix)。
其中前綴的碼值需要根據(jù)level_prefix的值去參照標準中的表9-6,后綴的碼值就是level_suffix的二進制。
?
如何計算level_prefix以及l(fā)evel_suffix的這部分比較麻煩,我們有必要照著標準分析。
標準規(guī)定了CAVLC解碼的步驟如下,我們能根據(jù)這些步驟逆推出它的編碼步驟
- The syntax element level_prefix is decoded as specified in clause 9.2.2.1.
- The variable levelSuffixSize is set as follows:
- If level_prefix is equal to 14 and suffixLength is equal to 0, levelSuffixSize is set equal to 4.
- Otherwise, if level_prefix is greater than or equal to 15, levelSuffixSize is set equal to level_prefix ? 3.
- Otherwise, levelSuffixSize is set equal to suffixLength.
- The syntax element level_suffix is decoded as follows:
- If levelSuffixSize is greater than 0, the syntax element level_suffix is decoded as unsigned integerrepresentation u(v) with levelSuffixSize bits.
- Otherwise (levelSuffixSize is equal to 0), the syntax element level_suffix is inferred to be equal to 0.
- The variable levelCode is set equal to ( Min( 15, level_prefix ) << suffixLength ) + level_suffix.
- When level_prefix is greater than or equal to 15 and suffixLength is equal to 0, levelCode is incremented by 15.
- When level_prefix is greater than or equal to 15( 16 is the same ), levelCode is incremented by (1<<( level_prefix ? 3 )) ? 4096.
- When the index i is equal to TrailingOnes( coeff_token ) and TrailingOnes( coeff_token ) is less than 3, levelCode is incremented by 2.
- The variable levelVal[ i ] is derived as follows:
- If levelCode is an even number, levelVal[ i ] is set equal to ( levelCode + 2 ) >> 1.
- Otherwise (levelCode is an odd number), levelVal[ i ] is set equal to ( ?levelCode ? 1) >> 1.
- When suffixLength is equal to 0, suffixLength is set equal to 1.
- When the absolute value of levelVal[ i ] is greater than ( 3 << ( suffixLength ? 1 ) ) and suffixLength is less than 6, suffixLength is incremented by 1.
- The index i is incremented by 1.
下面的分析過程會把這些步驟記為“上述步驟”
?
1. 有符號的level轉(zhuǎn)換為無符號的levelCode
我們知道level的值可能為正,也可能為負,是帶符號的,我們需要把它轉(zhuǎn)化成無符號的levelCode以供后面的計算。根據(jù)(上述步驟8),我們知道在進行CAVLC編碼時levelCode的計算方式如下:
如果$level$為正,$levelCode = (|level|-1)<<1$
如果$level$為負,$levelCode = (|level|-1)<<1+1$
因此有以下轉(zhuǎn)換
可以看到正的level變成了levelCode中的偶數(shù)部分,負的level變成了levelCode的奇數(shù)部分,及l(fā)evelCode中的最低位的bit代表符號。
此外,存在一種需要調(diào)整levelCode大小的情況:如果當前編碼塊的拖尾系數(shù)的個數(shù)小于3(TrailingOnes < 3),那么需要對第一個進行編碼的非零非拖尾level的levelCode減去2(上述步驟7)。
?
?
2. levelCode的拆分
這部分我們首先需要了解這兩個變量的含義
levelSuffixSize: 后綴level_suffix的實際長度
suffixLength: 在某些情況下,levelSuffixSize等于這個變量的值,suffixLength也用于計算前綴的值,它貫穿于整個CAVLC編碼過程,會在一個宏塊開始編碼level時進行初始化(見下述標準中抓取的部分),并且在每個level編碼后進行更新。
- If TotalCoeff( coeff_token ) is greater than 10 and TrailingOnes( coeff_token ) is less than 3, suffixLength is set
equal to 1. - Otherwise (TotalCoeff( coeff_token ) is less than or equal to 10 or TrailingOnes( coeff_token ) is equal to 3),
suffixLength is set equal to 0.
suffixLength會根據(jù)宏塊的非零系數(shù)以及拖尾系數(shù)的個數(shù)被初始化為0或者1;在每個level編碼后的更新,如果此時suffixLength為0,則suffixLength加1(上述步驟9),這意味著我們在對一個宏塊進行CAVLC時,最多只有一次是suffixLength為0;如果此時已被編碼的level大于( 3 << ( suffixLength ? 1 ) ) ,suffixLength也會加1(上述步驟10)。
?
根據(jù)上述標準,在拆分levelCode上我們可以分成兩大類情況
1. suffixLength為0
其中可細分為三種情況進行編碼
①? 如果$| level |<8$,那么levelSuffixSize為0,也就是沒有后綴level_suffix,這些level分別為$\pm 1,\pm 2,…,\pm 7$,這14個level由$level\_prefix = 0,1,2,…,13$表示。(上述步驟2第3條)
②? 如果$8 \leqslant | level |<16$,那么levelSuffixSize為4,也就是后綴共有四個bit,也就是說后綴能表達16個level,由于此時固定了$level\_prefix=14$,因此這種情況能表達的level就是16個,即$\pm 8,\pm 9,…,\pm 15$。(上述步驟2第1條)
③? 否則,$level\_prefix\geqslant 15$。這種情況下,levelSuffixSize的值為level_prefix-3(上述步驟2第2條)。此時levelCode在解碼端的計算方法為
$\begin{align*}
levelCode
&=\underbrace{ \left\lfloor 15, level\_prefix \right\rfloor << suffixLength? + level\_suffix}_{step.4} \quad \underbrace{+15}_{step.5}\quad \underbrace{+1<<(level\_prefix-3)-4096}_{step.6}\\
&=15<<0+level\_suffix+15+ 1<<(level\_prefix-3)-4096\\
&=level\_suffix+1<<(level\_prefix-3)-4066
\end{align*}$?
其中l(wèi)evel_prefix的值為$15,16,…$,在編碼端,levelCode已知,我們只需要按照遞增順序一個一個帶入level_prefix,并且保證level_suffix在其所占用 的bit的范圍內(nèi),即可得出合適的level_prefix與level_suffix。
?
JM18.6代碼如下
int writeSyntaxElement_Level_VLC1(SyntaxElement *se, DataPartition *dp, int profile_idc)
{int level = se->value1;int sign = (level < 0 ? 1 : 0);int levabs = iabs(level);if (levabs < 8) //① level_prefix < 14 ((8-1)*2 - len(inf) = 14 - 1){se->len = levabs * 2 + sign - 1;se->inf = 1;}else if (levabs < 16) //② level_prefix = 14 (len - len(inf) = 19 - len(16) = 19 - 5 = 14){// escape code1se->len = 19;se->inf = 16 | ((levabs << 1) - 16) | sign;}else //③ level_prefix >= 15 (28 - len(4096) + numPrefix = 15 + numPrefix){int iMask = 4096, numPrefix = 0;int levabsm16 = levabs + 2032;// escape code2 select level_prefix, which level_prefix = 15 + numPrefixif ((levabsm16) >= 4096){numPrefix++;while ((levabsm16) >= (4096 << numPrefix)){numPrefix++;}}iMask <<= numPrefix;se->inf = iMask | ((levabsm16 << 1) - iMask) | sign;/* caution : int levabs = iabs(level);** (levabsm16 << 1) - iMask* = ((levabs + 2032) << 1) - (4096 << numPrefix)* = ((iabs(level) + 2032) << 1) - (4096 << numPrefix)* = ((iabs(level) - 1 + 2033) << 1) - (1 << levelSuffixSize)* = ((iabs(level) - 1 + 2048 - 15) << 1) - (1 << levelSuffixSize)* = ((iabs(level) - 1 - 15) << 1) + 4096 - (1 << levelSuffixSize)* = ((iabs(level) - 1 - 15) << 1) - ((1 << levelSuffixSize) - 4096)** belonging to above,you can find the relationship from the spec*//* Assert to make sure that the code fits in the VLC *//* make sure that we are in High Profile to represent level_prefix > 15 */if (numPrefix > 0 && !is_FREXT_profile( profile_idc )){//error( "level_prefix must be <= 15 except in High Profile\n", 1000 );se->len = 0x0000FFFF; // This can be some other big numberreturn (se->len);}se->len = 28 + (numPrefix << 1);}symbol2vlc(se);writeUVLC2buffer(se, dp->bitstream);if(se->type != SE_HEADER)dp->bitstream->write_flag = 1;#if TRACEif(dp->bitstream->trace_enabled)trace2out (se);
#endifreturn (se->len);
} ?
2. suffixLength為N (N = 1,2,3,4,5,6)
suffixLength不為0的情況下,可細分為以下兩種編碼過程
①? 如果$levelCode<( 15<< suffixLength )$,為什么這里會有個15呢?因為15表明當前l(fā)evel所需要的level_prefix小于15,即(0~14)共15個數(shù)值。此時
$\begin{Bmatrix}
levelSuffixSize=suffixLength\qquad\qquad\qquad\qquad\qquad\quad & (step.2.1) \\
levelCode = level\_prefix<<suffixLength + level\_suffix & (step.4)
\end{Bmatrix}$
按照這兩個條件,得到
$\begin{Bmatrix}
level\_prefix &= &levelCode >> suffixLength \\
level\_suffix &= &levelCode \ \& \ \underbrace{11...1}_{suffixLength}
\end{Bmatrix}$
?
②? 否則,表明編碼需要的level_prefix大于或等于15。$levelSuffixSize = level\_prefix-3 \quad $(上述步驟2第2條)。解碼規(guī)定了此時的levelCode為
$\begin{align*}
levelCode
&=\underbrace{ \left\lfloor 15, level\_prefix \right\rfloor << suffixLength? + level\_suffix}_{step.4} \quad \underbrace{+1<<(level\_prefix-3)-4096}_{step.6}\\
&=15<<suffixLength + level\_suffix + 1<<(level\_prefix-3)-4096
\end{align*}$?
在編碼level端,levelCode與suffixLength已知,我們只需要按照遞增順序一個一個帶入level_prefix,并且保證level_suffix在其所占用 的bit的范圍內(nèi),即可得出合適的level_prefix與level_suffix。
?
JM18.6代碼如下
int writeSyntaxElement_Level_VLCN(SyntaxElement *se, int vlc, DataPartition *dp, int profile_idc)
{ int level = se->value1;int sign = (level < 0 ? 1 : 0);int levabs = iabs(level) - 1; int shift = vlc - 1; int escape = (15 << shift); //level_prefix = 15if (levabs < escape) //① level_prefix < 15{int sufmask = ~((0xffffffff) << shift);int suffix = (levabs) & sufmask;se->len = ((levabs) >> shift) + 1 + vlc;se->inf = (2 << shift) | (suffix << 1) | sign;}else // ② {int iMask = 4096;int levabsesc = levabs - escape + 2048; //+2048 * 2 = +4096 , on decode endpoint, would -4096int numPrefix = 0;if ((levabsesc) >= 4096) //level_prefix = 15 + numPrefix{numPrefix++;while ((levabsesc) >= (4096 << numPrefix)){numPrefix++;}}iMask <<= numPrefix;se->inf = iMask | ((levabsesc << 1) - iMask) | sign;/* caution : levabs = iabs(level) - 1; * * (levabsesc << 1) - iMask* = ((levabs - escape + 2048) << 1) - (4096 << numPrefix)* = ((iabs(level) - 1 - escape) << 1) + 4096 - (1 << levelSuffixSize)* = ((iabs(level) - 1 - escape) << 1) - ((1 << levelSuffixSize) - 4096)** belonging to above,you can find the relationship from the spec*//* Assert to make sure that the code fits in the VLC *//* make sure that we are in High Profile to represent level_prefix > 15 */if (numPrefix > 0 && !is_FREXT_profile( profile_idc )){//error( "level_prefix must be <= 15 except in High Profile\n", 1000 );se->len = 0x0000FFFF; // This can be some other big numberreturn (se->len);}se->len = 28 + (numPrefix << 1);}symbol2vlc(se);writeUVLC2buffer(se, dp->bitstream);if(se->type != SE_HEADER)dp->bitstream->write_flag = 1;#if TRACEif(dp->bitstream->trace_enabled)trace2out (se);
#endifreturn (se->len);
}
?
3. 更新suffixLength
上面我們說過,suffixLength貫穿整個宏塊非零level的編碼過程,它在一開始會被初始化,然后在每個level編碼完成后進行更新。更新遵循兩個規(guī)則:
- 如果suffixLength為0,則更新為1。(上述步驟9)
- 如果當前編碼的level有$level<( 3<< suffixLength -1 )$,并且$suffixLength<6$,則suffixLength++。(上述步驟10)
CAVLC在編碼非零level這部分,首先編碼順序就是從高頻到低頻進行,一般來說高頻level會比較小,低頻level較大,因此CAVLC中的做法就是令suffixLength初始化為0,或者1,后續(xù)基于上下文對suffixLength進行增加,這樣做是為了出現(xiàn)頻率更高的高頻的level占用更小的bit,這也符合變長編碼的思想。
?
?
?
四. 對TotalZeros進行編碼
TotalZeros:? 最后一個非零系數(shù)前零的總數(shù)目,如下圖例子為TotalZeros = 3,碼值需要根據(jù)當前宏塊的TotalCoeffs以及TotalZeros查表得到。
需要注意的是,在對Chroma DC進行CAVLC時,Chroma DC塊可能為2x2塊(4:2:0)或者2x4塊(4:2:2),這些都是要查不同的表格的。
?
?
五. 對RunBefore進行編碼
RunBefore:? 這個值是對于非零系數(shù)來說的,表示的是當前非零系數(shù)前面連續(xù)出現(xiàn)的0的個數(shù)
ZeroLeft:? 這個值也是對于非零系數(shù)來說的,表示的是當前非零系數(shù)前的0的總個數(shù),在一開始這個值會被賦值為TotalZeros。當ZeroLeft為0的時候意味著后續(xù)非零系數(shù)的RunBefore都為0,編碼結(jié)束。
我們根據(jù)RunBefore與ZeroLeft的值查表9-10得到碼值。編碼順序也是逆序進行,編碼次數(shù)最多為TotalCoeffs - 1。
如上述例子
編碼過程如下:
ZeroLeft = 3,RunBefore = 1,碼值為10
ZeroLeft = 2,RunBefore = 0,碼值為1
ZeroLeft = 2,RunBefore = 0,碼值為1
ZeroLeft = 2,RunBefore = 2,碼值為00
ZeroLeft = 0,RunBefore = 0,不需要碼流表示
轉(zhuǎn)載于:https://www.cnblogs.com/TaigaCon/p/5255018.html
總結(jié)
- 上一篇: dnf年套多少钱
- 下一篇: 好听的四个字名字女孩