【HEVC学習と研究】28、第1フレームの第1マクロブロックのSAO部分完全解析結果
以前の研究を経て,この最初のマクロブロックSAOを符号から構文要素値にストリームする解析過程をほぼ完全に整理した.ここにはあまり原理的な部分がなく、もっと多くの流水帳のように、記録について話しています.
コードでは、まず、先頭データを解析した後、現在のNALに解析されたコードストリームがあることを確認します.やはり、私たちがずっと使用していたこのdemoシーケンスの符号化結果を見てみましょう.コードストリームの正式な用語は、ストライプデータの値を解析します.
以上は,sliceの各CTUのSAO情報とCoding Quardtree情報を含む,現在のNALで解析されたデータである.具体的な実装手順は次のとおりです.
1、sliceデータの解析はTDecTop::xDecodeSlice()によって実行され、この関数はTDecGop::decompressSlice()を呼び出して具体的な仕事を実現する.この関数ではTDecSbac::resetEntropy()関数が呼び出され、末尾のTDecBinCABAC::start()関数では、コードストリームの最初の2文字206および103にm_が付与されるuiValueはUInt値の低い2桁としてm_uiValueの値は52839を初期値とする.
2、TDecGop::decompressSlice()はTDecSlice::decompresSlice()を呼び出し、SAOを解析する部分はTDecSbac::parseSaoOneLcuInterleaving()で実現する.この関数のパラメータの1つはSAOParam構造体ポインタpSaoParamであり、この構造体の定義は以下の通りである.
この構成では、
TDecSbac::parseSaoOneLcuInterleaving()解析の内容はSAOParamタイプsaoLcuParam[3]配列に与えられ、定義は以下の通りである.
TDecSbac::parseSaoOneLcuInterleaving()関数は、まずpSaoParamパラメータを初期化します.
現在のCTUはsliceの最初のCTUであり、左側と上の要素は存在しないため、parseSaoMerge()関数は実行されません.続いて、3回のforループにおいて、呼び出し方法はparseSaoOffset(&(pSaoParam->saoLcuParam[iCompIdx][iAddr])、iCompIdx)である.
1回目のループ:
parseSaoType()を呼び出し、解析結果pSaoParam->saoLcuParam[0][0].typeIdx = -1;pSaoParam->saoLcuParam[0][0].length = 0.
2回目のループ:
parseSaoType()を呼び出し、解析結果pSaoParam->saoLcuParam[1][0].typeIdx = 4;pSaoParam->saoLcuParam[0][0].length = 4;
4回のparseSaoMaxUvlc()をループ呼び出し、解析結果はpSaoParam->saoLcuParam[1][0].offset[0] = 0; pSaoParam->saoLcuParam[1][0].offset[1]=3で、解析中にこのセグメントコードストリームの3番目の文字「162」を読み出す. pSaoParam->saoLcuParam[1][0].offset[2] = 1; pSaoParam->saoLcuParam[1][0].offset[3] = 1;
4つのoffset値のうちゼロ以外のものについては、次のbitを復号し、0以外の値であれば、そのoffsetは逆になる.修正後、pSaoParam->saoLcuParam[1][0].offset[3] = -1;
parseSaoUflc(5,uiSymbol)関数を呼び出し、この関数は符号ストリームの4番目の文字"107"を読み出し、解析結果pSaoParam->saoLcuParam[1][0]となる.subTypeIdx = 13;
3回目のループ:
compidxは2であるため、parseSaoType()は呼び出されず、pSaoParam->saoLcuParamとなる[2][0].typeIdx = pSaoParam->saoLcuParam[1][0].typeIdx = 4;
4回のparseSaoMaxUvlc()をループ呼び出し、解析結果はpSaoParam->saoLcuParam[2][0].offset[0] = 0; pSaoParam->saoLcuParam[2][0].offset[1] = 1;
pSaoParam->saoLcuParam[2][0].offset[2]=3で、解析中にコードストリームの次の文字「167」を読み出す. pSaoParam->saoLcuParam[2][0].offset[3] = 2;
4つのoffset値のうちゼロ以外のものについては、次のbitを復号し、0以外の値であれば、そのoffsetは逆になる.修正後、pSaoParam->saoLcuParam[1][0].offset[1] = -1; pSaoParam->saoLcuParam[2][0].offset[2]=3で、このbitを解析するときに次の文字「87」を読み出す.pSaoParam->saoLcuParam[2][0].offset[3] = -2;
parseSaoUflc(5,uiSymbol)関数を呼び出し、解析結果pSaoParam->saoLcuParam[2][0].subTypeIdx = 10;
これでTDecSbac::parseSaoOneLcuInterleaving()の最初のCTUのSAO部分解析が完了した.
コードでは、まず、先頭データを解析した後、現在のNALに解析されたコードストリームがあることを確認します.やはり、私たちがずっと使用していたこのdemoシーケンスの符号化結果を見てみましょう.コードストリームの正式な用語は、ストライプデータの値を解析します.
206 103 162 107 167 87 243 112 29 35 44 3 245 69 197 199 130 168 75 91 169 13 159 38 44 174 148 159 37 172 43 217 73 169 33 74 88 146 195 80 196 231 214 59 238 104 42 137 56 252 115 11 102 195 85 4 47 66 181 232 246 222 92 26 84 152 189 7 62 243 15 16 231 158 235 4 90 252 47 41 204 119 249 104 7 178 137 231 213 62 100 80 122 148 87 35 46 139 55 177 40 227 175 3 129 248 70 5 219 190 208 79 169 200 205 61 36 197 240 51 42 243 229 201 62 49 207 2 104 181 68 53 25 98 248 54 26 176 237 220 224 72 115 168 55 148 140 140 197 203 62 189 25 189 183 69 172 218 189 37 249 242 225 138 233 55 202 66 0 136 113 212 253 71 92 157 212 121 84 53 48 69 117 229 187 241 18 60 42 35 101 161 142 2 247 106 43 25 92 141 181 219 34 71 6 98 42 245 84 140 62 252 245 190 24 ......
以上は,sliceの各CTUのSAO情報とCoding Quardtree情報を含む,現在のNALで解析されたデータである.具体的な実装手順は次のとおりです.
1、sliceデータの解析はTDecTop::xDecodeSlice()によって実行され、この関数はTDecGop::decompressSlice()を呼び出して具体的な仕事を実現する.この関数ではTDecSbac::resetEntropy()関数が呼び出され、末尾のTDecBinCABAC::start()関数では、コードストリームの最初の2文字206および103にm_が付与されるuiValueはUInt値の低い2桁としてm_uiValueの値は52839を初期値とする.
2、TDecGop::decompressSlice()はTDecSlice::decompresSlice()を呼び出し、SAOを解析する部分はTDecSbac::parseSaoOneLcuInterleaving()で実現する.この関数のパラメータの1つはSAOParam構造体ポインタpSaoParamであり、この構造体の定義は以下の通りである.
struct SAOParam
{
Bool bSaoFlag[2];
SAOQTPart* psSaoPart[3];
Int iMaxSplitLevel;
Bool oneUnitFlag[3];
SaoLcuParam* saoLcuParam[3];
Int numCuInHeight;
Int numCuInWidth;
~SAOParam();
};
この構成では、
TDecSbac::parseSaoOneLcuInterleaving()解析の内容はSAOParamタイプsaoLcuParam[3]配列に与えられ、定義は以下の通りである.
typedef struct _SaoLcuParam
{
Bool mergeUpFlag;
Bool mergeLeftFlag;
Int typeIdx;
Int subTypeIdx; ///< indicates EO class or BO band position
Int offset[4];
Int partIdx;
Int partIdxTmp;
Int length;
} SaoLcuParam;
TDecSbac::parseSaoOneLcuInterleaving()関数は、まずpSaoParamパラメータを初期化します.
for (Int iCompIdx=0; iCompIdx<3; iCompIdx++)
{
pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeUpFlag = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeLeftFlag = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].subTypeIdx = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].typeIdx = -1;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[0] = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[1] = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[2] = 0;
pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[3] = 0;
}
現在のCTUはsliceの最初のCTUであり、左側と上の要素は存在しないため、parseSaoMerge()関数は実行されません.続いて、3回のforループにおいて、呼び出し方法はparseSaoOffset(&(pSaoParam->saoLcuParam[iCompIdx][iAddr])、iCompIdx)である.
1回目のループ:
parseSaoType()を呼び出し、解析結果pSaoParam->saoLcuParam[0][0].typeIdx = -1;pSaoParam->saoLcuParam[0][0].length = 0.
2回目のループ:
parseSaoType()を呼び出し、解析結果pSaoParam->saoLcuParam[1][0].typeIdx = 4;pSaoParam->saoLcuParam[0][0].length = 4;
4回のparseSaoMaxUvlc()をループ呼び出し、解析結果はpSaoParam->saoLcuParam[1][0].offset[0] = 0; pSaoParam->saoLcuParam[1][0].offset[1]=3で、解析中にこのセグメントコードストリームの3番目の文字「162」を読み出す. pSaoParam->saoLcuParam[1][0].offset[2] = 1; pSaoParam->saoLcuParam[1][0].offset[3] = 1;
4つのoffset値のうちゼロ以外のものについては、次のbitを復号し、0以外の値であれば、そのoffsetは逆になる.修正後、pSaoParam->saoLcuParam[1][0].offset[3] = -1;
parseSaoUflc(5,uiSymbol)関数を呼び出し、この関数は符号ストリームの4番目の文字"107"を読み出し、解析結果pSaoParam->saoLcuParam[1][0]となる.subTypeIdx = 13;
3回目のループ:
compidxは2であるため、parseSaoType()は呼び出されず、pSaoParam->saoLcuParamとなる[2][0].typeIdx = pSaoParam->saoLcuParam[1][0].typeIdx = 4;
4回のparseSaoMaxUvlc()をループ呼び出し、解析結果はpSaoParam->saoLcuParam[2][0].offset[0] = 0; pSaoParam->saoLcuParam[2][0].offset[1] = 1;
pSaoParam->saoLcuParam[2][0].offset[2]=3で、解析中にコードストリームの次の文字「167」を読み出す. pSaoParam->saoLcuParam[2][0].offset[3] = 2;
4つのoffset値のうちゼロ以外のものについては、次のbitを復号し、0以外の値であれば、そのoffsetは逆になる.修正後、pSaoParam->saoLcuParam[1][0].offset[1] = -1; pSaoParam->saoLcuParam[2][0].offset[2]=3で、このbitを解析するときに次の文字「87」を読み出す.pSaoParam->saoLcuParam[2][0].offset[3] = -2;
parseSaoUflc(5,uiSymbol)関数を呼び出し、解析結果pSaoParam->saoLcuParam[2][0].subTypeIdx = 10;
これでTDecSbac::parseSaoOneLcuInterleaving()の最初のCTUのSAO部分解析が完了した.