欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

HEVC帧内预测HM实现

程序员文章站 2022-07-07 12:27:52
...

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;

HEVC帧内预测HM实现

 

 

HEVC帧内预测HM实现

结合之前的角度图 和  角度表。 上面一段代码的功能就是 根据模式来 取 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];

                }

HEVC帧内预测HM实现

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

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

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

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

 

HEVC帧内预测HM实现


    

            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, 所有应该行列转置一下