HEVC帧内预测HM实现

1、具体代码位置

角度预测的代码路径:TLibCommon\TComPrediction.cpp  xPredIntraAng()


2、代码流程

 

        const Bool       bIsModeVer = (dirMode >= 18);
        const Int        intraPredAngleMode = (bIsModeVer) ? (Int)dirMode - VER_IDX :  -((Int)dirMode - HOR_IDX);
        const Int        absAngMode = abs(intraPredAngleMode);
        const Int        signAng = intraPredAngleMode < 0 ? -1 : 1;
        const Bool       edgeFilter = bEnableEdgeFilters && isLuma(channelType) &&  (width <= MAXIMUM_INTRA_FILTERED_WIDTH) && (height <= MAXIMUM_INTRA_FILTERED_HEIGHT);
        // Set bitshifts and scale the angle parameter to block size
        static const Int angTable[9] = { 0,    2,    5,   9,  13,  17,  21,  26,  32  };
        static const Int invAngTable[9] = { 0, 4096, 1638, 910, 630, 482, 390, 315,  256 }; // (256 * 32) / Angle
        Int invAngle = invAngTable[absAngMode];
        Int absAng = angTable[absAngMode];
        Int intraPredAngle = signAng * absAng;

 

 

结合之前的角度图 和  角度表。 上面一段代码的功能就是 根据模式来 取 intraPredAngle 角度。

首先以 18 为分界 分水平 和垂直, 大于等于18 垂直, 小于水平。

水平和 垂直 分类的角度中 又可以以10 为分界 分为 水平向上 向下, 垂直又可以以26为分界分为垂直向左 向右

水平的2-17 从10 往两边角度增加相同的即 2 5 9 13 17 21 26 32。 垂直的18-34 从26 往两边角度增加相同的即 2 5 9 13 17 21 26 32。

所以只要定义这个 就可以把所有模式的角度给计算出来。

intraPredAngleMode = (bIsModeVer) ? (Int)dirMode - VER_IDX : -((Int)dirMode -  HOR_IDX);

这个就是计算距离 10 和 26 的差值,absAngMode 这个是距离的绝对值, signAng这个就是符合 是向上 向下 向左 还是向右。


                const Int refMainOffsetPreScale = (bIsModeVer ? height : width) - 1;

                const Int refMainOffset = height - 1;

                for (Int x = 0; x < width + 1; x++)

                {

                        refAbove[x + refMainOffset] = pSrc[x - srcStride - 1];

                }

                for (Int y = 0; y < height + 1; y++)

                {

                        refLeft[y + refMainOffset] = pSrc[(y - 1)*srcStride - 1];

                }

                refMain = (bIsModeVer ? refAbove : refLeft) + refMainOffset;

                refSide = (bIsModeVer ? refLeft : refAbove) + refMainOffset;

                // Extend the Main reference to the left.

                Int invAngleSum = 128;       // rounding for (shift by 8)

                for (Int k = -1; k > (refMainOffsetPreScale + 1)*intraPredAngle >> 5;  k--)

                {

                        invAngleSum += invAngle;

                        refMain[k] = refSide[invAngleSum >> 8];

                }

inAngle 的作用, 相当于 是一个算法定点化的一个过程。

这边实际就是之前说过的映射的过程,refMain 主参考边的数组,对于垂直类的模式,主参考边是refAbove ,水平类的就是refLeft。

主参考边的数组确定之后,剩下的一个就是副参考 refSide。

最后的循环就是要将refSide 映射到 refMain。 也就是下面这个公式,就是将refSide一一映射到refMain。

 


    

            Pel *pDsty = pDst;

                for (Int y = 0, deltaPos = intraPredAngle; y < height; y++, deltaPos  += intraPredAngle, pDsty += dstStride)

                {

                        const Int deltaInt = deltaPos >> 5;

                        const Int deltaFract = deltaPos & (32 - 1);

                        if (deltaFract)

                        {

                               // Do linear filtering

                               const Pel *pRM = refMain + deltaInt + 1;

                               Int lastRefMainPel = *pRM++;

                               for (Int x = 0; x < width; pRM++, x++)

                               {

                                      Int thisRefMainPel = *pRM;

                                      pDsty[x + 0] = (Pel)(((32 -  deltaFract)*lastRefMainPel + deltaFract * thisRefMainPel + 16) >> 5);

                                      lastRefMainPel = thisRefMainPel;

                               }

                        }

                        else

                        {

                               // Just copy the integer samples

                               for (Int x = 0; x < width; x++)

                               {

                                      pDsty[x] = refMain[x + deltaInt + 1];

                               }

                        }

                }

上面这个就是参考像素获取的代码。 最外层的循环 是 y = 0 到 height -1。deltaPos每次都是增加intraPredAngle,pDsty 也是增加dstStride。

算出deltaInt 这个用来确定位置,另外一个则是权重。

refMain 指针移动deltaInt + 1 个字节 pRM指向这个位置。 然后先把这个位置的值取出来赋值给lastRefMainPel,pRM 向前移动一位。

然后在内层循环里面。块中行扫描。  thisRefMainPel 取 pRM 当前指向的值。  预测值 就取lastRefMainPel和thisRefMainPel 的权重。

然后将thisRefMainPel 赋值给lastRefMainPel, pRM 向前移动一位 thisRefMainPel 取pRM。 这样循环。

index的变化 简化就是外面循环加intra_pred_angle,算delta_int,里面的循环delta_int的基础上面取累积。

           for (int j = 0; j < iHeight; j++) {

                      delta_pos = delta_pos + intra_pred_angle;

                      delta_int = delta_pos >> 5;

                      for (int i = 0; i < iWidth; i++) {

                             int ref_main_index = i + delta_int;

                             int ref_side_index = ref_main_index + 1;

                             saveDistanceMatri(distanMatri, i, j, ref_main_index,  ref_side_index);

                      }

              }







         if (!bIsModeVer)

         {

                for (Int y = 0; y < height; y++)

                {

                        for (Int x = 0; x < width; x++)

                        {

                               pTrueDst[x*dstStrideTrue] = pDst[x];

                        }

                        pTrueDst++;

                        pDst += dstStride;

                }

         }


   

x方向和y方向之间的关系

垂直方向是按行扫描过去  然后得到的结果一个个存放在 pDsty中。 

一行都是相差1的。

水平方向的 是需要按列是相差1的。  但是现在 按照行做完,pDsty 中却是行相差1, 所有应该行列转置一下

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章