HEVC函数入门(4)——指针,帧内预测,滤波

来源:互联网 发布:大淘客联盟与淘宝联盟 编辑:程序博客网 时间:2024/06/15 18:01

在http://blog.csdn.net/shaqoneal/article/details/38963083这篇博客中讲到了获取参考数据的指针 ,指向的是当前块的参考数据,但是在我的16.3版本中有所不同。
原博两篇的题目分别为HEVC参考软件HM中Intra预测参考像素的获取与管理和帧内预测参考数据的获取和滤波处理 感觉一部分是通过边界来预测当前PU内像素值,而另一个部分是获取边界值。恩暂时这么理解吧(纯属个人理解,后面如果发现理解错了,会返回这边修改)。
这些内容是跟着上面的博客内容做的,非常多的重复,这是由于还没有搞懂HM和HEVC的一些情况,先按照大神的博客走一遍,再写一遍博客加强一下吧。之后会再总结CJL大神的博客。恩,先这样O(∩_∩)O哈哈~
代表帧内预测几种模式的函数xPredIntraPlanar、xPredIntraAng和xDCPredFiltering调用的位置位于Void TComPrediction::predIntraLumaAng()中,所以也可以说,在一个PU内,函数Void TComPrediction::predIntraLumaAng实现了亮度分量的帧内预测。
在16.3中使用了

 const Pel *ptrSrc = getPredictorPtr( compID, false ); 

找到它的定义

 Void    initTempBuff(ChromaFormat chromaFormatIDC); ChromaFormat getChromaFormat() const { return m_cYuvPredTemp.getChromaFormat(); }  // inter Void motionCompensation         ( TComDataCU*  pcCU, TComYuv* pcYuvPred, RefPicList eRefPicList = REF_PIC_LIST_X, Int iPartIdx = -1 );  // motion vector prediction Void getMvPredAMVP              ( TComDataCU* pcCU, UInt uiPartIdx, UInt uiPartAddr, RefPicList eRefPicList, TComMv& rcMvPred );  // Angular Intra Void predIntraAng               ( const ComponentID compID, UInt uiDirMode, Pel *piOrg /* Will be null for decoding */, UInt uiOrgStride, Pel* piPred, UInt uiStride, TComTU &rTu, Bool bAbove, Bool bLeft, const Bool bUseFilteredPredSamples, const Bool bUseLosslessDPCM = false ); Pel  predIntraGetPredValDC      ( const Pel* pSrc, Int iSrcStride, UInt iWidth, UInt iHeight, ChannelType channelType, ChromaFormat format, Bool bAbove, Bool bLeft ); Pel*  getPredictorPtr           ( const ComponentID compID, const Bool bUseFilteredPredictions )  {return m_piYuvExt[compID][bUseFilteredPredictions?PRED_BUF_FILTERED:PRED_BUF_UNFILTERED];//返回数组指针  }// This function is actually still in TComPattern.cpp// set parameters from CU data for accessing ADI data  Void initAdiPatternChType ( TComTU &rTu,Bool&       bAbove,Bool&       bLeft,const ComponentID compID, const Bool bFilterRefSamplesDEBUG_STRING_FN_DECLARE(sDebug)                              );  static Bool filteringIntraReferenceSamples(const ComponentID compID, UInt uiDirMode, UInt uiTuChWidth, UInt uiTuChHeight, const ChromaFormat chFmt, const Bool intraReferenceSmoothingDisabled);  static Bool UseDPCMForFirstPassIntraEstimation(TComTU &rTu, const UInt uiDirMode);};//! \}#endif // __TCOMPREDICTION__

getPredictorPtr所操作的数据地址指针,其实就是m_piYuvExt。看来文章就在这个指针变量中了。m_piYuvExt定义在TComPrediction类中,在其构造函数中初始化,在析构函数中释放内存。分配响应的内存空间在函数Void
TComPrediction::initTempBuff()中实现,这个函数在编码开始之前就会被调用。
实际的参考数据呢?实际上,在对当前PU的每一种模式进行遍历(TEncSearch::estIntraPredQT函数)之前,会有专门操作对m_piYuvExt进行数据填充操作,具体的操作在TComPattern::initAdiPattern中实现。该函数比较长就不贴在这里了,里面的核心部分是调用了fillReferenceSamples函数填充参考数据,随后生成Intra预测的滤波参考数据。

这个函数呢在16.3中我是通过搜索找到的:叫做initAdiPatternChType和原博有点不同,前面我们讲到的是PU的部分,这里是TU的部分,如果这里不对,我会再编辑修正。
这里写图片描述
跳转到定义后,打开了一个 TComPrediction.cpp的文件,找到fillReferenceSamples方法。之前我们在预测中,打开的是TEncSearch.cpp和TComPrediction.cpp两个,前面讲到的函数(不包括fillReferenceSamples)都在其中。
fillReferenceSamples在HM16.3中和原博的HM9有很大的区别,但是其概念是一样的,先放函数,最后放其概念,其方法如下:

#if O0043_BEST_EFFORT_DECODINGVoid fillReferenceSamples( const Int bitDepth, const Int bitDepthDelta, TComDataCU* pcCU, const Pel* piRoiOrigin, Pel* piAdiTemp, const Bool* bNeighborFlags,#elseVoid fillReferenceSamples( const Int bitDepth, TComDataCU* pcCU, const Pel* piRoiOrigin, Pel* piAdiTemp, const Bool* bNeighborFlags,#endif                           const Int iNumIntraNeighbor, const Int unitWidth, const Int unitHeight, const Int iAboveUnits, const Int iLeftUnits,                           const UInt uiCuWidth, const UInt uiCuHeight, const UInt uiWidth, const UInt uiHeight, const Int iPicStride,                           const ChannelType chType, const ChromaFormat chFmt ){  const Pel* piRoiTemp;  Int  i, j;  Int  iDCValue = 1 << (bitDepth - 1);  const Int iTotalUnits = iAboveUnits + iLeftUnits + 1; //+1 for top-left  if (iNumIntraNeighbor == 0)//所欲参考点均不可得,按照DC模式设置参考点    {    // Fill border with DC value 用DC值填充边界    for (i=0; i<uiWidth; i++)    {      piAdiTemp[i] = iDCValue; //<span style="font-family: Arial, Helvetica, sans-serif;">piAdiTemp指向数据接收内存,保存了实际的参考像素数组的地址;</span>      }    for (i=1; i<uiHeight; i++)    {      piAdiTemp[i*uiWidth] = iDCValue;    }  }  else if (iNumIntraNeighbor == iTotalUnits) //所有参考点都可获得,直接设为当前CU的参考值   {    // Fill top-left border and top and top right with rec. samples  top-left border为左上角边界,也就是CU左上一个点    piRoiTemp = piRoiOrigin - iPicStride - 1;  //左上角边界,其实就是CU左上角的一个点    for (i=0; i<uiWidth; i++)     {#if O0043_BEST_EFFORT_DECODING      piAdiTemp[i] = piRoiTemp[i] << bitDepthDelta; #else      piAdiTemp[i] = piRoiTemp[i];#endif    }    // Fill left and below left border with rec. samples     piRoiTemp = piRoiOrigin - 1; //当前CU左上顶点的左边像素     for (i=1; i<uiHeight; i++)     {#if O0043_BEST_EFFORT_DECODING      piAdiTemp[i*uiWidth] = (*(piRoiTemp)) << bitDepthDelta;#else      piAdiTemp[i*uiWidth] = *(piRoiTemp);#endif      piRoiTemp += iPicStride;    }  }  else // reference samples are partially available  {    // all above units have "unitWidth" samples each, all left/below-left units have "unitHeight" samples each    const Int  iTotalSamples = (iLeftUnits * unitHeight) + ((iAboveUnits + 1) * unitWidth);    Pel  piAdiLine[5 * MAX_CU_SIZE];    Pel  *piAdiLineTemp;    const Bool *pbNeighborFlags;    // Initialize    for (i=0; i<iTotalSamples; i++)  //用均值模式进行初始化      {      piAdiLine[i] = iDCValue;    }    // Fill top-left sample    piRoiTemp = piRoiOrigin - iPicStride - 1;//指向重建像素中当前CU的左上角位置      piAdiLineTemp = piAdiLine + (iLeftUnits * unitHeight);    pbNeighborFlags = bNeighborFlags + iLeftUnits;    if (*pbNeighborFlags) //如果左上方的参考数据可用     {#if O0043_BEST_EFFORT_DECODING      Pel topLeftVal=piRoiTemp[0] << bitDepthDelta;#else      Pel topLeftVal=piRoiTemp[0];#endif      for (i=0; i<unitWidth; i++)      {        piAdiLineTemp[i] = topLeftVal;      }    }    // Fill left & below-left samples (downwards)    piRoiTemp += iPicStride; //从左上顶点的左上角移动到左方     piAdiLineTemp--;    pbNeighborFlags--;    for (j=0; j<iLeftUnits; j++)    {      if (*pbNeighborFlags)      {        for (i=0; i<unitHeight; i++)        {#if O0043_BEST_EFFORT_DECODING          piAdiLineTemp[-i] = piRoiTemp[i*iPicStride] << bitDepthDelta;#else          piAdiLineTemp[-i] = piRoiTemp[i*iPicStride];#endif        }      }      piRoiTemp += unitHeight*iPicStride;      piAdiLineTemp -= unitHeight;      pbNeighborFlags--;    }    // Fill above & above-right samples (left-to-right) (each unit has "unitWidth" samples) 水平方向上的处理与垂直方向类似      piRoiTemp = piRoiOrigin - iPicStride;    // offset line buffer by iNumUints2*unitHeight (for left/below-left) + unitWidth (for above-left)    piAdiLineTemp = piAdiLine + (iLeftUnits * unitHeight) + unitWidth;    pbNeighborFlags = bNeighborFlags + iLeftUnits + 1;    for (j=0; j<iAboveUnits; j++)    {      if (*pbNeighborFlags)      {        for (i=0; i<unitWidth; i++)        {#if O0043_BEST_EFFORT_DECODING          piAdiLineTemp[i] = piRoiTemp[i] << bitDepthDelta;#else          piAdiLineTemp[i] = piRoiTemp[i];#endif        }      }      piRoiTemp += unitWidth;      piAdiLineTemp += unitWidth;      pbNeighborFlags++;    }    // Pad reference samples when necessary    Int iCurrJnit = 0;    Pel  *piAdiLineCur   = piAdiLine;//指向参考数组的起点,见下图      const UInt piAdiLineTopRowOffset = iLeftUnits * (unitHeight - unitWidth);    if (!bNeighborFlags[0])    {      // very bottom unit of bottom-left; at least one unit will be valid.      {        Int   iNext = 1;        while (iNext < iTotalUnits && !bNeighborFlags[iNext])// 找到第一个可以获得的点         {          iNext++;        }        Pel *piAdiLineNext = piAdiLine + ((iNext < iLeftUnits) ? (iNext * unitHeight) : (piAdiLineTopRowOffset + (iNext * unitWidth)));        const Pel refSample = *piAdiLineNext;         // Pad unavailable samples with new value        Int iNextOrTop = std::min<Int>(iNext, iLeftUnits);        // fill left column        while (iCurrJnit < iNextOrTop) //将找到的可用参考点赋给第一个参考点(以4个像素点一组为单位)          {          for (i=0; i<unitHeight; i++)          {            piAdiLineCur[i] = refSample;          }          piAdiLineCur += unitHeight;          iCurrJnit++;        }        // fill top row        while (iCurrJnit < iNext) //将找到的可用参考点赋给第一个参考点(以4个像素点一组为单位)          {          for (i=0; i<unitWidth; i++)          {            piAdiLineCur[i] = refSample;          }          piAdiLineCur += unitWidth;          iCurrJnit++;        }      }    }    // pad all other reference samples.    while (iCurrJnit < iTotalUnits) //遍历给定的参考点    {      if (!bNeighborFlags[iCurrJnit]) // samples not available 某个点不可获得        {        {          const Int numSamplesInCurrUnit = (iCurrJnit >= iLeftUnits) ? unitWidth : unitHeight;          const Pel refSample = *(piAdiLineCur-1);          for (i=0; i<numSamplesInCurrUnit; i++)          {            piAdiLineCur[i] = refSample;          }          piAdiLineCur += numSamplesInCurrUnit;          iCurrJnit++;        }      }      else      {        piAdiLineCur += (iCurrJnit >= iLeftUnits) ? unitWidth : unitHeight;        iCurrJnit++;      }    }    // Copy processed samples 输出前面所准备的数据     piAdiLineTemp = piAdiLine + uiHeight + unitWidth - 2;    // top left, top and top right samples    for (i=0; i<uiWidth; i++)    {      piAdiTemp[i] = piAdiLineTemp[i];    }    piAdiLineTemp = piAdiLine + uiHeight - 1;    for (i=1; i<uiHeight; i++)    {      piAdiTemp[i*uiWidth] = piAdiLineTemp[-i];    }  }}

上面函数的标注是我参考原来的博文http://blog.csdn.net/shaqoneal/article/details/39183735标注的,不一定正确,希望可以和各位交流。
下面,是概念:

帧内预测的参考像素值的获取在标准文档的8.4.4.2.2中指明。
举例说明,当前demo中,我们用来单步调试的第一个CU为64×64像素大小,那么参考像素由两部分组成,一部分包含2×64+1=129个,另一部分包含2×64=128个像素。这两部分分别作为垂直和水平方向上的预测数据。在编码的过程中,根据预测数据是否可得,共分为两种情况:
第一种:所有的预测数据都不可得。最直观的情况就是一帧数据中的第一个CU,该CU左侧和上方的数据都不存在,如下图所示。此时所有的预测数据都会制定一个默认值,计算方法为:1 << (bitDepth - 1);(图中的格子数只是示意图,不代表CU的像素大小和参考像素的个数)。

这里写图片描述

第二种:至少有一个像素点是可获得的,如下图所示。如果参考数据中的第一个点是不可获得的,那么将沿着当前CU的边缘,先从下到上,后从左到右查找第一个可获得的参考点并赋给第一个点;对于其他的点,如果不可得,那么就直接复制它前面一个参考点的值。如果所有点都是可获得的,那么参考数据直接使用该值就可以了。
这里写图片描述
上面这些我的理解:从左下开始时,如果从第一个点就不可用,那么就用第一个可用的点填充第一个点,第一个点后面的不可用的点,用前面的点填充
具体内容我是参照:http://blog.csdn.net/linpengbin/article/details/48005413
http://blog.csdn.net/linpengbin/article/details/44158331

阅读全文
0 0
原创粉丝点击