[置顶] HEVC学习(二十三) —— 熵编码之四

本文主要考察与概率转移有关的几个表格及使用分析。

下图是draft 9.3.4.2中的Figure 9-7,在接下来的分析中需要用到,这里先贴出来:

[置顶] HEVC学习(二十三) —— 熵编码之四_第1张图片

(注:图中有个小错误,valMPS = 1 ? valMPS,实际应为valMPS = 1 - valMPS,特此更正)

在ContextModel.cpp中,给出了transIdxLPS和transIdxMPS这两个表格:

const UChar ContextModel::m_aucNextStateMPS[ 128 ] =
{
  2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
  18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
  34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
  50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
  66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
  82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
  98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
  114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 124, 125, 126, 127
};

const UChar ContextModel::m_aucNextStateLPS[ 128 ] =
{
  1, 0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 8, 9, 10, 11,
  12, 13, 14, 15, 16, 17, 18, 19, 18, 19, 22, 23, 22, 23, 24, 25,
  26, 27, 26, 27, 30, 31, 30, 31, 32, 33, 32, 33, 36, 37, 36, 37,
  38, 39, 38, 39, 42, 43, 42, 43, 44, 45, 44, 45, 46, 47, 48, 49,
  48, 49, 50, 51, 52, 53, 52, 53, 54, 55, 54, 55, 56, 57, 58, 59,
  58, 59, 60, 61, 60, 61, 60, 61, 62, 63, 64, 65, 64, 65, 66, 67,
  66, 67, 66, 67, 68, 69, 68, 69, 70, 71, 70, 71, 70, 71, 72, 73,
  72, 73, 72, 73, 74, 75, 74, 75, 74, 75, 76, 77, 76, 77, 126, 127
};

不难发现,HM中给出的这两个表格跟draft里的Table 9-41是不一样的:

[置顶] HEVC学习(二十三) —— 熵编码之四_第2张图片

这是为什么呢?我的前面一篇分析ContextModel::init的博客有提到,HM中是将pStateIdx和valMPS合并到一个新的变量中去的:

  UChar getState  ()                { return ( m_ucState >> 1 ); }                    ///< get current state
  UChar getMps    ()                { return ( m_ucState  & 1 ); }                    ///< get curret MPS
  Void  setStateAndMps( UChar ucState, UChar ucMPS) { m_ucState = (ucState << 1) + ucMPS; } ///< set state and MPS

因此,对于上述的两个表格,只要将每个元素值右移1位即可得到实际的pStateIdx,如下所示:

[置顶] HEVC学习(二十三) —— 熵编码之四_第3张图片
每两个值对应于Table 9-41中的一个,有兴趣不妨一个个对照着检验一下。

于是,下面这两个函数就实现了Figure 9-7中的两个概率更新过程:

  Void updateLPS ()
  {
    m_ucState = m_aucNextStateLPS[ m_ucState ]; //!< pStateIdx = transIdxLPS[pStateIdx]
  }
  
  Void updateMPS ()
  {
    m_ucState = m_aucNextStateMPS[ m_ucState ]; //!< pStateIdx = transIdxMPS[pStateIdx]
  }

接下来分析的是HM9.1中使用的基于JCTVC-G763提案的Table-based bit estimation for CABAC,简单地说,这个提案是为了在一开始模式选择时熵编码使用估计值(以查表的方式)而不是直接进行编码,以此在不增加码率的前提下提高编码速度,有关详细内容,可查阅具体提案,本文不作展开。相关函数实现如下所示:

#if FAST_BIT_EST
  Void update( Int binVal )
  {
    m_ucState = m_nextState[m_ucState][binVal];
  }
  static Void buildNextStateTable();
  static Int getEntropyBitsTrm( Int val ) { return m_entropyBits[126 ^ val]; }
#endif

重点关注m_nextState中的值是如何设置的,看buildNextStateTable:

#if FAST_BIT_EST
UChar ContextModel::m_nextState[128][2];

Void ContextModel::buildNextStateTable()
{
  for (Int i = 0; i < 128; i++)
  {
    for (Int j = 0; j < 2; j++)
    {
      m_nextState[i][j] = ((i&1) == j) ? m_aucNextStateMPS[i] : m_aucNextStateLPS[i];
    }
  }
}
#endif

打印出来的m_nextState的值如下所示(由于数据较多,仅列出前面一小部分的值):

0:	MPS: 2(1)		LPS: 1(0)	
1:	LPS: 0(0)		MPS: 3(1)	
2:	MPS: 4(2)		LPS: 0(0)	
3:	LPS: 1(0)		MPS: 5(2)	
4:	MPS: 6(3)		LPS: 2(1)	
5:	LPS: 3(1)		MPS: 7(3)	
6:	MPS: 8(4)		LPS: 4(2)	
7:	LPS: 5(2)		MPS: 9(4)	
8:	MPS: 10(5)		LPS: 4(2)	
9:	LPS: 5(2)		MPS: 11(5)	
10:	MPS: 12(6)		LPS: 8(4)	
11:	LPS: 9(4)		MPS: 13(6)	
12:	MPS: 14(7)		LPS: 8(4)	
13:	LPS: 9(4)		MPS: 15(7)	
14:	MPS: 16(8)		LPS: 10(5)	
15:	LPS: 11(5)		MPS: 17(8)	
16:	MPS: 18(9)		LPS: 12(6)	
17:	LPS: 13(6)		MPS: 19(9)	
18:	MPS: 20(10)		LPS: 14(7)	
19:	LPS: 15(7)		MPS: 21(10)	
20:	MPS: 22(11)		LPS: 16(8)	
21:	LPS: 17(8)		MPS: 23(11)	
22:	MPS: 24(12)		LPS: 18(9)


为了下面讨论问题的方便,我在对数据添加了注释和相应的处理,其中,第一列的数字是m_NextState的第一个索引值,第二、第三列分别对应m_NextState第二个索引值为0、1的情况。MPS代表该值是由m_aucNextStateMPS赋值过来的,LPS代表该值是由m_aucNextStateLPS赋值过来的,冒号后的数字就是这两张状态转移表的值,而括号中的值则是将状态值右移1位的结果,从而得到实际的pStateIdx。

我们看看HM中使用m_NextState的地方:

  Void update( Int binVal )
  {
    m_ucState = m_nextState[m_ucState][binVal];
  }

第一个值对应的是pStateIdx,第二个值对应的就是输入的二进制值。

举实例来分析这个表格:

(1)先举一般情况,即pStateIdx不等于0(m_ucState不等于0或1)的情况,不妨设m_ucState = 9,valMPS=1,则此时pStateIdx=4。如果直接根据Table 9-41来索引,对于binVal=0,则pStateIdx=transIdxLPS[4]=2;对于binVal=1,则pStateIdx=transIdxMPS[4]=5。我们来看看用m_NextState索引是否也是这个结果。

           对于binVal=0,m_ucState=m_NextState[9][0]=5,则pStateIdx=2;

           对于binVal=1,m_ucState=m_NextState[9][1]=11,则pStateIdx=5。

由上可见,结果与使用Table 9-41的结果完全一致。

(2)再看极端的情况,即pState等于0(m_ucState等于0或1)的情况,不妨设m_ucState=1,valMPS=1,则此时pStateIdx=0,根据Table 9-41,对于binVal=0,则,valMPS=1-1=0,pStateIdx=transIdxLPS[0]=0;对于binVal=1,则,pStateIdx=transIdxMPS[0]=1。下面看看使用m_NextState的情况:

           对于binVal=0,m_ucState=m_NextState[1][0]=0,则pStateIdx=0;

           对于binVal=1,m_ucState=m_NextState[1][1]=3,则pStateIdx=1;

结果也与Table 9-41的结果一致。

 

对于其它m_ucState,valMPS的值的情况,可作类似分析,这里不再一一列举。


 

你可能感兴趣的:(hm,HEVC,CABAC,熵编码)