H.266/VVC代码学习15:LM模式代码(xGetLumaRecPixels、predIntraChromaLM)

LM模式简单来说是:用亮度的左边和(或)上边建立一个Y-C的线性模型,再把这样预测的pu中的每个像素都套入这个模型,根据已预测好的Y值和这个线性模型得到C值。

分类:分为CCLM(色度模式67,用左边和上边)、MDLM-T(色度模式68,只用上边)、MDLM-L(色度模式69,只用左边)三种模式。
入口函数:xIntraCodingTUBlock。详见:H.266/VVC代码学习9:xIntraCodingTUBlock函数
下面我们分阶段进行学习,请结合注释查阅VTM4.0中的代码。

进入LM模式的代码如下:

    //===== get prediction signal 计算预测值 =====
    if( compID != COMPONENT_Y && PU::isLMCMode( uiChFinalMode ) )//2.LM模式预测,得到预测角度
    {
      {
        xGetLumaRecPixels( pu, area );//下采样,获取重建像素
      }
      predIntraChromaLM( compID, piPred, pu, area, uiChFinalMode );//LM模式预测
    }
void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred, const PredictionUnit &pu, const CompArea& chromaArea, int intraDir)
{
  /**********************************设置参数***********************************************/
  int  iLumaStride = 0;	//下一列的值
  PelBuf Temp;			//存宽高stride三个值
  if ((intraDir == MDLM_L_IDX) || (intraDir == MDLM_T_IDX))//MDLM用的是两个行的长度
  {
    iLumaStride = 2 * MAX_CU_SIZE + 1;
    Temp = PelBuf(m_pMdlmTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
  }
  else//CCLM用的是两个行的长度
  {
	iLumaStride = MAX_CU_SIZE + 1;
	Temp = PelBuf(m_piTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
  }

  /**************************** 下采样已结束,这里可以得到a和b ******************************/
  int a, b, iShift;
  xGetLMParameters(pu, compID, chromaArea, a, b, iShift);//后续重点

  /********** 最终预测,用a、b做的线性模型,计算得这个CU中全部像素的色度值,与模式无关 **********/
  piPred.copyFrom(Temp);
  piPred.linearTransform(a, iShift, b, true, pu.cs->slice->clpRng(compID));//计算:pred(C) = a * rec(L) + b
}

1 下采样:xGetLumaRecPixels

主要工作:将亮度2Nx2N的块,用1:2:1下采样变成亮度NxN的亮度块。
目的:为了能和其对应的NxN的色度块一一对应。

void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chromaArea)//这个函数是做下采样,用以重建亮度值
{
	/********************************************** 初始化 ******************************************************/
  int iDstStride = 0;// ==》下采样后的一行
  Pel* pDst0 = 0;//=========》下采样后的缓存
  int curChromaMode = pu.intraDir[1];
  if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX))//MMLM的操作
  {
    iDstStride = 2 * MAX_CU_SIZE + 1;
    pDst0 = m_pMdlmTemp + iDstStride + 1;
  }
  else//CCLM的操作
  {
	iDstStride = MAX_CU_SIZE + 1;
	pDst0 = m_piTemp + iDstStride + 1; //MMLM_SAMPLE_NEIGHBOR_LINES;
  }
  //assert 420 chroma subsampling
  CompArea lumaArea = CompArea( COMPONENT_Y, pu.chromaFormat, chromaArea.lumaPos(), recalcSize( pu.chromaFormat, CHANNEL_TYPE_CHROMA, CHANNEL_TYPE_LUMA, chromaArea.size() ) );// 亮度块 needed for correct pos/size (4x4 Tus)


  CHECK( lumaArea.width  == chromaArea.width, "" );
  CHECK( lumaArea.height == chromaArea.height, "" );

  const SizeType uiCWidth = chromaArea.width;
  const SizeType uiCHeight = chromaArea.height;

  const CPelBuf Src = pu.cs->picture->getRecoBuf( lumaArea );//原始
  Pel const* pRecSrc0   = Src.bufAt( 0, 0 );//======》下采样前的缓存
  int iRecStride        = Src.stride;
  int iRecStride2       = iRecStride << 1;// ==》下采样前的一行

  const CodingUnit& lumaCU = isChroma( pu.chType ) ? *pu.cs->picture->cs->getCU( lumaArea.pos(), CH_L ) : *pu.cu;//亮度CU
  const CodingUnit&     cu = *pu.cu;//CU

  const CompArea& area = isChroma( pu.chType ) ? chromaArea : lumaArea;

  const uint32_t uiTuWidth  = area.width;//TU宽
  const uint32_t uiTuHeight = area.height;//TU高

  int iBaseUnitSize = ( 1 << MIN_CU_LOG2 );

  const int  iUnitWidth       = iBaseUnitSize >> getComponentScaleX( area.compID, area.chromaFormat );
  const int  iUnitHeight      = iBaseUnitSize >> getComponentScaleX( area.compID, area.chromaFormat );
  const int  iTUWidthInUnits  = uiTuWidth  / iUnitWidth;
  const int  iTUHeightInUnits = uiTuHeight / iUnitHeight;
  const int  iAboveUnits      = iTUWidthInUnits;//上边用几个
  const int  iLeftUnits       = iTUHeightInUnits;
  const int  chromaUnitWidth = iBaseUnitSize >> getComponentScaleX(COMPONENT_Cb, area.chromaFormat);
  const int  chromaUnitHeight = iBaseUnitSize >> getComponentScaleX(COMPONENT_Cb, area.chromaFormat);
  /* 下面是对MDLM的定义 */
  const int  topTemplateSampNum = 2 * uiCWidth; // for MDLM, the number of template samples is 2W or 2H.
  const int  leftTemplateSampNum = 2 * uiCHeight;
  assert(m_topRefLength >= topTemplateSampNum);
  assert(m_leftRefLength >= leftTemplateSampNum);
  const int  totalAboveUnits = (topTemplateSampNum + (chromaUnitWidth - 1)) / chromaUnitWidth;//右上用几个
  const int  totalLeftUnits = (leftTemplateSampNum + (chromaUnitHeight - 1)) / chromaUnitHeight;
  const int  totalUnits = totalLeftUnits + totalAboveUnits + 1;
  const int  aboveRightUnits = totalAboveUnits - iAboveUnits;//右上用几个
  const int  leftBelowUnits = totalLeftUnits - iLeftUnits;//左下用                                                                                                   几个

  /**/
  int avaiAboveRightUnits = 0;
  int avaiLeftBelowUnits = 0;
  bool  bNeighborFlags[4 * MAX_NUM_PART_IDXS_IN_CTU_WIDTH + 1];
  memset(bNeighborFlags, 0, totalUnits);
  bool bAboveAvaillable, bLeftAvaillable;

  /**************************** 确定上边和左边是否可用,1:2:1下采样上边和左边(就是特殊处理下边边角角) ****************************/
  int availlableUnit = isLeftAvailable( isChroma( pu.chType ) ? cu : lumaCU, toChannelType( area.compID ), area.pos(), iLeftUnits, iUnitHeight,
  ( bNeighborFlags + iLeftUnits + leftBelowUnits - 1 ) );

  bLeftAvaillable = availlableUnit == iTUHeightInUnits;

  availlableUnit = isAboveAvailable( isChroma( pu.chType ) ? cu : lumaCU, toChannelType( area.compID ), area.pos(), iAboveUnits, iUnitWidth,
  ( bNeighborFlags + iLeftUnits + leftBelowUnits + 1 ) );

  bAboveAvaillable = availlableUnit == iTUWidthInUnits;

  if (bLeftAvaillable) // if left is not available, then the below left is not available
  {
    avaiLeftBelowUnits = isBelowLeftAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.bottomLeftComp(area.compID), leftBelowUnits, iUnitHeight, (bNeighborFlags + leftBelowUnits - 1));
  }

  if (bAboveAvaillable) // if above is not available, then  the above right is not available.
  {
    avaiAboveRightUnits = isAboveRightAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.topRightComp(area.compID), aboveRightUnits, iUnitWidth, (bNeighborFlags + iLeftUnits + leftBelowUnits + iAboveUnits + 1));
  }

  Pel*       pDst  = nullptr;
  Pel const* piSrc = nullptr;

  bool isFirstRowOfCtu = ((pu.block(COMPONENT_Cb).y)&(((pu.cs->sps)->getMaxCUWidth() >> 1) - 1)) == 0;

  /*** 如果不是第一行,那么上可用,可以处理第一行重建的 ***/
  if( bAboveAvaillable )
  {
    pDst  = pDst0    - iDstStride;
    int addedAboveRight = 0;
    if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX))
    {
      addedAboveRight = avaiAboveRightUnits*chromaUnitWidth;
    }
    for (int i = 0; i < uiCWidth + addedAboveRight; i++)
    {
      if (isFirstRowOfCtu)
      {
        piSrc = pRecSrc0 - iRecStride;

        if (i == 0 && !bLeftAvaillable)
        {
          pDst[i] = piSrc[2 * i];
        }
        else
        {
          pDst[i] = ( piSrc[2 * i] * 2 + piSrc[2 * i - 1] + piSrc[2 * i + 1] + 2 ) >> 2;
        }
      }
#if JVET_M0142_CCLM_COLLOCATED_CHROMA
      else if( pu.cs->sps->getCclmCollocatedChromaFlag() )
      {
        piSrc = pRecSrc0 - iRecStride2;

        if( i == 0 && !bLeftAvaillable )
        {
          pDst[i] = ( piSrc[2 * i] * 2 + piSrc[2 * i - iRecStride] + piSrc[2 * i + iRecStride] + 2 ) >> 2;
        }
        else
        {
          pDst[i] = ( piSrc[2 * i - iRecStride]
                    + piSrc[2 * i             ] * 4 + piSrc[2 * i - 1] + piSrc[2 * i + 1]
                    + piSrc[2 * i + iRecStride]
                    + 4 ) >> 3;
        }
      }
#endif
      else
      {
        piSrc = pRecSrc0 - iRecStride2;

        if (i == 0 && !bLeftAvaillable)
        {
          pDst[i] = ( piSrc[2 * i] + piSrc[2 * i + iRecStride] + 1 ) >> 1;
        }
        else
        {
          pDst[i] = ( ( ( piSrc[2 * i             ] * 2 ) + piSrc[2 * i - 1             ] + piSrc[2 * i + 1             ] )
                    + ( ( piSrc[2 * i + iRecStride] * 2 ) + piSrc[2 * i - 1 + iRecStride] + piSrc[2 * i + 1 + iRecStride] )
                    + 4 ) >> 3;
        }
      }
    }
  }
  /*** 如果不是第一列,那么左可用,可以处理第一列重建的 ***/
  if( bLeftAvaillable )
  {
    pDst  = pDst0    - 1;
    piSrc = pRecSrc0 - 3;
    int addedLeftBelow = 0;
    if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX))
    {
      addedLeftBelow = avaiLeftBelowUnits*chromaUnitHeight;
    }
    for (int j = 0; j < uiCHeight + addedLeftBelow; j++)
    {
#if JVET_M0142_CCLM_COLLOCATED_CHROMA
      if( pu.cs->sps->getCclmCollocatedChromaFlag() )
      {
        if( j == 0 && !bAboveAvaillable )
        {
          pDst[0] = ( piSrc[1] * 2 + piSrc[0] + piSrc[2] + 2 ) >> 2;
        }
        else
        {
          pDst[0] = ( piSrc[1 - iRecStride]
                    + piSrc[1             ] * 4 + piSrc[0] + piSrc[2]
                    + piSrc[1 + iRecStride]
                    + 4 ) >> 3;
        }
      }
      else
      {
#endif
        pDst[0] = ( ( piSrc[1             ] * 2 + piSrc[0         ] + piSrc[2             ] )
                  + ( piSrc[1 + iRecStride] * 2 + piSrc[iRecStride] + piSrc[2 + iRecStride] )
                  + 4 ) >> 3;
#if JVET_M0142_CCLM_COLLOCATED_CHROMA
      }
#endif

      piSrc += iRecStride2;
      pDst  += iDstStride;
    }
  }


  /*********************************************** 1:2:1下采样除了边缘的内部部分 *****************************************/
  // inner part from reconstructed picture buffer 重建图片缓冲区的内部部分
  for( int j = 0; j < uiCHeight; j++ )
  {
    for( int i = 0; i < uiCWidth; i++ )
    {
#if JVET_M0142_CCLM_COLLOCATED_CHROMA
      if( pu.cs->sps->getCclmCollocatedChromaFlag() )
      {
        if( i == 0 && !bLeftAvaillable )//处理
        {
          if( j == 0 && !bAboveAvaillable )
          {
            pDst0[i] = pRecSrc0[2 * i];
          }
          else
          {
            pDst0[i] = ( pRecSrc0[2 * i] * 2 + pRecSrc0[2 * i - iRecStride] + pRecSrc0[2 * i + iRecStride] + 2 ) >> 2;
          }
        }
        else if( j == 0 && !bAboveAvaillable )
        {
          pDst0[i] = ( pRecSrc0[2 * i] * 2 + pRecSrc0[2 * i - 1] + pRecSrc0[2 * i + 1] + 2 ) >> 2;
        }
        else
        {
          pDst0[i] = ( pRecSrc0[2 * i - iRecStride]
                     + pRecSrc0[2 * i             ] * 4 + pRecSrc0[2 * i - 1] + pRecSrc0[2 * i + 1]
                     + pRecSrc0[2 * i + iRecStride]
                     + 4 ) >> 3;
        }
      }
      else
      {
#endif
        if( i == 0 && !bLeftAvaillable )
        {
          pDst0[i] = ( pRecSrc0[2 * i] + pRecSrc0[2 * i + iRecStride] + 1 ) >> 1;
        }
        else
        {
          pDst0[i] = ( pRecSrc0[2 * i             ] * 2 + pRecSrc0[2 * i + 1             ] + pRecSrc0[2 * i - 1             ]
                     + pRecSrc0[2 * i + iRecStride] * 2 + pRecSrc0[2 * i + 1 + iRecStride] + pRecSrc0[2 * i - 1 + iRecStride]
                     + 4 ) >> 3;
        }
#if JVET_M0142_CCLM_COLLOCATED_CHROMA
      }
#endif
    }

    pDst0    += iDstStride;		//Dst处理一行或一列
    pRecSrc0 += iRecStride2;	//Src要跳过两行或两列
  }
}

2 构建模型:xGetLMParameters

主要工作:根据NxN的亮度块的左边和上边像素,取点,找到Y的最大值和最小值点,根据他们的C值求得线性模型参数a和b。
目的:求得a、b为了后面使用该线性模型进行预测像素的色度值。

void IntraPrediction::xGetLMParameters(const PredictionUnit &pu, const ComponentID compID,
                                              const CompArea &chromaArea,
                                              int &a, int &b, int &iShift)//这里面是第一步:建立模型得到a和b
{
	/************************************************ 初始化 ******************************************/
  CHECK(compID == COMPONENT_Y, "");

  const SizeType cWidth  = chromaArea.width;
  const SizeType cHeight = chromaArea.height;

  const Position posLT = chromaArea;

  CodingStructure & cs = *(pu.cs);
  const CodingUnit &cu = *(pu.cu);

  const SPS &        sps           = *cs.sps;
  const uint32_t     tuWidth     = chromaArea.width;
  const uint32_t     tuHeight    = chromaArea.height;
  const ChromaFormat nChromaFormat = sps.getChromaFormatIdc();

  const int baseUnitSize = 1 << MIN_CU_LOG2;
  const int unitWidth    = baseUnitSize >> getComponentScaleX(chromaArea.compID, nChromaFormat);
  const int unitHeight   = baseUnitSize >> getComponentScaleX(chromaArea.compID, nChromaFormat);

  const int tuWidthInUnits  = tuWidth / unitWidth;
  const int tuHeightInUnits = tuHeight / unitHeight;
  const int aboveUnits      = tuWidthInUnits;//上面可以用几个采样点。宽若是8则这个值一般为4。后续左边同理
  const int leftUnits       = tuHeightInUnits;
  int topTemplateSampNum = 2 * cWidth; // for MDLM, the template sample number is 2W or 2H;
  int leftTemplateSampNum = 2 * cHeight;
  assert(m_topRefLength >= topTemplateSampNum);
  assert(m_leftRefLength >= leftTemplateSampNum);
  int totalAboveUnits = (topTemplateSampNum + (unitWidth - 1)) / unitWidth;//上面最多可用几个采样点。宽若是8则这个值一般为4*2=8
  int totalLeftUnits = (leftTemplateSampNum + (unitHeight - 1)) / unitHeight;
  int totalUnits = totalLeftUnits + totalAboveUnits + 1;
  int aboveRightUnits = totalAboveUnits - aboveUnits;//右上可以用几个采样点。宽若是8则这个值一般为 4*2-4 = 4
  int leftBelowUnits = totalLeftUnits - leftUnits;
  /*下面四个变量在后面赋值并参与运算*/
  int avaiAboveRightUnits = 0;
  int avaiLeftBelowUnits = 0;
  int avaiAboveUnits = 0;
  int avaiLeftUnits = 0;

  int curChromaMode = pu.intraDir[1];//看当前模式是67,68还是69
  bool neighborFlags[4 * MAX_NUM_PART_IDXS_IN_CTU_WIDTH + 1];
  memset(neighborFlags, 0, totalUnits);

  /*********************************************** 看左和上都是否可用 ******************************************/
  bool aboveAvailable, leftAvailable;

  int availableUnit =//临时定义的,为了看后面上和左是否可用
    isAboveAvailable(cu, CHANNEL_TYPE_CHROMA, posLT, aboveUnits, unitWidth,
    (neighborFlags + leftUnits + leftBelowUnits + 1));
  aboveAvailable = availableUnit == tuWidthInUnits;//上是否可用(上面是不是边缘)

  availableUnit =
    isLeftAvailable(cu, CHANNEL_TYPE_CHROMA, posLT, leftUnits, unitHeight,
    (neighborFlags + leftUnits + leftBelowUnits - 1));
  leftAvailable = availableUnit == tuHeightInUnits;//左是否可用(左面是不是边缘)
  if (leftAvailable) // if left is not available, then the below left is not available,左不可用,左下一定不可用
  {
    avaiLeftUnits = tuHeightInUnits;
    avaiLeftBelowUnits = isBelowLeftAvailable(cu, CHANNEL_TYPE_CHROMA, chromaArea.bottomLeftComp(chromaArea.compID), leftBelowUnits, unitHeight, (neighborFlags + leftBelowUnits - 1));
  }
  if (aboveAvailable) // if above is not available, then  the above right is not available.上不可用,上右一定不可用
  {
    avaiAboveUnits = tuWidthInUnits;
    avaiAboveRightUnits = isAboveRightAvailable(cu, CHANNEL_TYPE_CHROMA, chromaArea.topRightComp(chromaArea.compID), aboveRightUnits, unitWidth, (neighborFlags + leftUnits + leftBelowUnits + aboveUnits + 1));
  }
  Pel *srcColor0, *curChroma0;
  int  srcStride, curStride;//一行的跨度

  PelBuf temp;
  if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX))
  {
    srcStride = 2 * MAX_CU_SIZE + 1;
    temp = PelBuf(m_pMdlmTemp + srcStride + 1, srcStride, Size(chromaArea));
  }
  else
  {
	srcStride = MAX_CU_SIZE + 1;
	temp        = PelBuf(m_piTemp + srcStride + 1, srcStride, Size(chromaArea));
  }
  srcColor0 = temp.bufAt(0, 0);
  curChroma0 = getPredictorPtr(compID);

  curStride = m_topRefLength + 1;

  curChroma0 += curStride + 1;

  unsigned internalBitDepth = sps.getBitDepth(CHANNEL_TYPE_CHROMA);

  /********** Y是横坐标,C是纵坐标。这里找到最左和最右两个点,即亮度值最小和最大的两个点,拿这个做线性模型 ********/
  int minLuma[2] = {  MAX_INT, 0 };//最左边的点:亮度值最小的点,数组中的两个值分别是{Y,C}
  int maxLuma[2] = { -MAX_INT, 0 };//最右边的点:亮度值最大的点,数组中的两个值分别是{Y,C}

  Pel *src = srcColor0 - srcStride;//这里对应亮度下采样后得到的buffer(NxN),减去是因为应指向往前的一行
  Pel *cur = curChroma0 - curStride;//这里色度CCLM预测后得到的buffer(NxN),减去是因为应指向往前的一行
  int minDim = 1;
  int actualTopTemplateSampNum = 0;
  int actualLeftTemplateSampNum = 0;
  if (curChromaMode == MDLM_T_IDX)//MDLM_T的情况:只用上*2
  {
    leftAvailable = 0;
    actualTopTemplateSampNum = unitWidth*(avaiAboveUnits + avaiAboveRightUnits);
	minDim = actualTopTemplateSampNum;
  }
  else if (curChromaMode == MDLM_L_IDX)//MDLM_L的情况:只用左*2
  {
    aboveAvailable = 0;
    actualLeftTemplateSampNum = unitHeight*(avaiLeftUnits + avaiLeftBelowUnits);
	minDim = actualLeftTemplateSampNum;
  }
  else if (curChromaMode == LM_CHROMA_IDX)//CCLM的情况:用左和上
  {
    actualTopTemplateSampNum = cWidth;
    actualLeftTemplateSampNum = cHeight;
	minDim = leftAvailable && aboveAvailable ? 1 << g_aucPrevLog2[std::min(actualLeftTemplateSampNum, actualTopTemplateSampNum)]
		: 1 << g_aucPrevLog2[leftAvailable ? actualLeftTemplateSampNum : actualTopTemplateSampNum];
  }

  /****************************** 取点,根据一一对应的亮度和色度找到最大最小 *************************************/
  int numSteps = minDim;//短边长:为了保证长边和短边取点数一致。如8*2,2上取2点,8上也得取2点

  if (aboveAvailable)//比较亮度大小,取出最大和最小亮度值及其对应色度值。
  {
    for (int j = 0; j < numSteps; j++)
    {
		int idx = (j * actualTopTemplateSampNum) / minDim;//步长:为了保证长边和短边取点数一致

      if (minLuma[0] > src[idx])
      {
        minLuma[0] = src[idx];//最小亮度对应的亮度值
        minLuma[1] = cur[idx];//最小亮度对应的色度值
      }
      if (maxLuma[0] < src[idx])
      {
        maxLuma[0] = src[idx];//最大亮度对应的亮度值
        maxLuma[1] = cur[idx];//最大亮度对应的色度值
      }
    }
  }

  if (leftAvailable)//代码和上很相似,操作也同上
  {
    src = srcColor0 - 1;
    cur = curChroma0 - 1;

    for (int i = 0; i < numSteps; i++)
    {
		int idx = (i * actualLeftTemplateSampNum) / minDim;

      if (minLuma[0] > src[srcStride * idx])
      {
        minLuma[0] = src[srcStride * idx];
        minLuma[1] = cur[curStride * idx];
      }
      if (maxLuma[0] < src[srcStride * idx])
      {
        maxLuma[0] = src[srcStride * idx];
        maxLuma[1] = cur[curStride * idx];
      }
    }
  }

  /**************************** 进行计算a b ,这里极为麻烦,暂时跳过 ************************************/
  if (leftAvailable || aboveAvailable)
  {
#if JVET_M0064_CCLM_SIMPLIFICATION
    int diff = maxLuma[0] - minLuma[0];
    if (diff > 0)
    {
      int diffC = maxLuma[1] - minLuma[1];
      int x = floorLog2( diff );
      static const uint8_t DivSigTable[1 << 4] = {
        // 4bit significands - 8 ( MSB is omitted )
        0,  7,  6,  5,  5,  4,  4,  3,  3,  2,  2,  1,  1,  1,  1,  0
      };
      int normDiff = (diff << 4 >> x) & 15;
      int v = DivSigTable[normDiff] | 8;
      x += normDiff != 0;

      int y = floorLog2( abs( diffC ) ) + 1;
      int add = 1 << y >> 1;
      a = (diffC * v + add) >> y;
      iShift = 3 + x - y;
      if ( iShift < 1 ) {
        iShift = 1;
        a = ( (a == 0)? 0: (a < 0)? -15 : 15 );   // a=Sign(a)*15
      }
      b = minLuma[1] - ((a * minLuma[0]) >> iShift);
    }
    else
    {
      a = 0;
      b = minLuma[1];
      iShift = 0;
    }
#else // original
    a         = 0;
    iShift    = 16;
    int shift = (internalBitDepth > 8) ? internalBitDepth - 9 : 0;
    int add   = shift ? 1 << (shift - 1) : 0;
    int diff  = (maxLuma[0] - minLuma[0] + add) >> shift;
    if (diff > 0)
    {
      int div = ((maxLuma[1] - minLuma[1]) * g_aiLMDivTableLow[diff - 1] + 32768) >> 16;
      a       = (((maxLuma[1] - minLuma[1]) * g_aiLMDivTableHigh[diff - 1] + div + add) >> shift);
    }
    b = minLuma[1] - ((a * minLuma[0]) >> iShift);
#endif
  }
  else
  {
    a = 0;

    b = 1 << (internalBitDepth - 1);

    iShift = 0;
  }
}

3 色度预测:piPred.linearTransform

主要工作:建立线性模型,通过该模型确定每个pu内所有像素的色度值。
目的:这个函数就是对数字的一系列操作。暂且开个坑,以后填(其实,嘿嘿,这个坑不敢保证后面会不会填了。。。。。)

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值