VTM3.0代码阅读:解码端coding_tree函数

coding_tree函数通过递归调用,寻址ctu中的所有cu,对每个cu调用coding_unit解码cu信息。
其实,coding_tree函数根据解码得到的QT和MT的flag,逐步递归划分当前区域直到cu,本质就是在解码端重建编码端的划分树,直到叶子节点解码cu。

本函数与VTM1中代码的大致流程一致,最大区别就是对>64的DualITree块进行特别处理:
对于>64的DualITree块,在QT时要对亮度和色度统一进行,保持QT对亮度和色度划分一致。
对于>64的DualITree块,本函数入口参数既有亮度partitioner,又有色度chromaPartitioner。
对于>64的DualITree块的情况时,最后会将luma和chroma的cu拆分为两条,并将chromaCU的头接到lumaCU的尾,有何必要?

bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CUCtx& cuCtx, Partitioner* pPartitionerChroma, CUCtx* pCuCtxChroma)
{
  const PPS      &pps         = *cs.pps;
  const UnitArea &currArea    = partitioner.currArea();		//当前处理的区域
  bool           lastSegment  = false;

  // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
  if( pps.getUseDQP() && partitioner.currDepth <= pps.getMaxCuDQPDepth() )
  {
    cuCtx.isDQPCoded          = false;
  }
  if( cs.slice->getUseChromaQpAdj() && partitioner.currDepth <= pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth() )
  {
    cuCtx.isChromaQpAdjCoded  = false;
  }

  // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
  if (CS::isDualITree(cs) && pPartitionerChroma != nullptr)//即:I帧DualITree且CTU为128x128时的情况
  {

    if (pps.getUseDQP() && pPartitionerChroma->currDepth <= pps.getMaxCuDQPDepth())
    {
      pCuCtxChroma->isDQPCoded = false;
    }
    if (cs.slice->getUseChromaQpAdj() && pPartitionerChroma->currDepth <= pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth())
    {
      pCuCtxChroma->isChromaQpAdjCoded = false;
    }
  }

  const PartSplit implicitSplit = partitioner.getImplicitSplit( cs );

  // QT
  bool canQtSplit = partitioner.canSplit( CU_QUAD_SPLIT, cs );		//当前区域是否满足qt划分条件

  if( canQtSplit )
  {
    // force QT split enabling on the edges and if the current area exceeds maximum transformation size
    bool qtSplit = implicitSplit == CU_QUAD_SPLIT;

    // split_cu_flag
    if( !qtSplit && implicitSplit != CU_QUAD_SPLIT )
    {
      qtSplit = split_cu_flag( cs, partitioner );		//解码QT划分的flag,当前是否进行qt划分
    }

    // quad-tree split
    if( qtSplit )
    {									//下面这个if中,>=64 应该修改为 >64
								//MTT在CTU的划分时,首先进行QT划分,QT叶子节点进行BT/TT划分,VVC中CTU一般为128x128
								//这一段代码,对I帧DualITree且CTU为128x128时的块进行限制,让亮度和色度在QT划分阶段保持一致
      if (CS::isDualITree(cs) && pPartitionerChroma != nullptr && (partitioner.currArea().lwidth() >= 64 || partitioner.currArea().lheight() >= 64))
      {
        partitioner.splitCurrArea(CU_QUAD_SPLIT, cs);
        pPartitionerChroma->splitCurrArea(CU_QUAD_SPLIT, cs);	//亮度和色度同时QT划分
        bool beContinue = true;
        bool lumaContinue = true;
        bool chromaContinue = true;
        bool lastSegmentC = false;

        while (beContinue)
        {
          if (partitioner.currArea().lwidth() > 64 || partitioner.currArea().lheight() > 64)
          {						//对大于64的DualITree块进行coding_tree时,亮度色度同时
            if (!lastSegmentC && cs.area.blocks[partitioner.chType].contains(partitioner.currArea().blocks[partitioner.chType].pos()))
            {
              lastSegmentC = coding_tree(cs, partitioner, cuCtx, pPartitionerChroma, pCuCtxChroma);	//既有亮度partitioner,又有色度chromaPartitioner
            }
            lumaContinue = partitioner.nextPart(cs);
            chromaContinue = pPartitionerChroma->nextPart(cs);
            CHECK(lumaContinue != chromaContinue, "luma chroma partition should be matched");
            beContinue = lumaContinue;
          }
          else
          {
            //dual tree coding under 64x64 block
            if (!lastSegment && cs.area.blocks[partitioner.chType].contains(partitioner.currArea().blocks[partitioner.chType].pos()))
            {					//对小于64的DualITree块进行coding_tree时,亮度和色度分开进行解码
              lastSegment = coding_tree(cs, partitioner, cuCtx);
            }
            lumaContinue = partitioner.nextPart(cs);
            if (!lastSegmentC && cs.area.blocks[pPartitionerChroma->chType].contains(pPartitionerChroma->currArea().blocks[pPartitionerChroma->chType].pos()))
            {
              lastSegmentC = coding_tree(cs, *pPartitionerChroma, *pCuCtxChroma);
            }
            chromaContinue = pPartitionerChroma->nextPart(cs);
            CHECK(lumaContinue != chromaContinue, "luma chroma partition should be matched");
            CHECK(lastSegment == true, "luma should not be the last segment");
            beContinue = lumaContinue;
          }
        }
        partitioner.exitCurrSplit();
        pPartitionerChroma->exitCurrSplit();		//至此已经将当前128x128块中的cu解码完全

        //cat the chroma CUs together
        CodingUnit* currentCu = cs.getCU(partitioner.currArea().lumaPos(), CHANNEL_TYPE_LUMA);	//得到当前块(一般为128x128,即CTU)第一个luma CU
        CodingUnit* nextCu = nullptr;
        CodingUnit* tempLastLumaCu = nullptr;
        CodingUnit* tempLastChromaCu = nullptr;
        ChannelType currentChType = currentCu->chType;		//算法写的挺好的
        while (currentCu->next != nullptr)
        {								//CS中是将luma和chroma的CU存储在一个vector中的,这里将luma和chroma的CU分开
          nextCu = currentCu->next;
          if (currentChType != nextCu->chType && currentChType == CHANNEL_TYPE_LUMA)
          {
            tempLastLumaCu = currentCu;
            if (tempLastChromaCu != nullptr) //swap
            {
              tempLastChromaCu->next = nextCu;		//将CS::cus[]的CU分成两条链表luma和chroma
            }
          }
          else if (currentChType != nextCu->chType && currentChType == CHANNEL_TYPE_CHROMA)
          {
            tempLastChromaCu = currentCu;
            if (tempLastLumaCu != nullptr) //swap
            {
              tempLastLumaCu->next = nextCu;		//将CS::cus[]的CU分成两条链表luma和chroma
            }
          }
          currentCu = nextCu;
          currentChType = currentCu->chType;
        }

        CodingUnit* chromaFirstCu = cs.getCU(pPartitionerChroma->currArea().chromaPos(), CHANNEL_TYPE_CHROMA);
        tempLastLumaCu->next = chromaFirstCu;				//lumaCU链表的尾接chromaCU的头

        lastSegment = lastSegmentC;
      }
      else			//P/B帧、I帧非DualITree、I帧块小于等于64
      {
		  partitioner.splitCurrArea( CU_QUAD_SPLIT, cs );		//当前区域qt划分为4分子区域
		  do
		  {
			if( !lastSegment && cs.area.blocks[partitioner.chType].contains( partitioner.currArea().blocks[partitioner.chType].pos() ) )
			{
			  lastSegment = coding_tree( cs, partitioner, cuCtx );		//子区域递归调用coding_tree,寻址解码其中的cu
			}
		  } while( partitioner.nextPart( cs ) );

		  partitioner.exitCurrSplit();
      }
      return lastSegment;			//当前区域解码完毕,return
    }
  }

  {
    // MT
    bool mtSplit = partitioner.canSplit( CU_MT_SPLIT, cs );		//是否可以mt划分

    if( mtSplit )
    {
      const PartSplit splitMode = split_cu_mode_mt( cs, partitioner );	//解码mt划分模式

      if( splitMode != CU_DONT_SPLIT )
      {
        partitioner.splitCurrArea( splitMode, cs );		//根据解码的mt划分模式,划分当前区域

        do
        {
          if( !lastSegment && cs.area.blocks[partitioner.chType].contains( partitioner.currArea().blocks[partitioner.chType].pos() ) )
          {
            lastSegment = coding_tree(cs, partitioner, cuCtx);	//子区域递归调用coding_tree,寻址解码其中的cu
          }
        } while( partitioner.nextPart( cs ) );

        partitioner.exitCurrSplit();
        return lastSegment;		//当前区域解码完毕,return
      }
    }
  }
			//如果当前区域解码上面的qt和mt的划分flag,均不再划分,则表示递归到了划分树的叶子节点,当前区域即为cu区域,开始解码cu

  CodingUnit& cu = cs.addCU( CS::getArea( cs, currArea, partitioner.chType ), partitioner.chType );

  partitioner.setCUData( cu );		//cs中添加cu,给它赋值depth等信息,接下来给它解码预测信息
  cu.slice   = cs.slice;
#if HEVC_TILES_WPP
  cu.tileIdx = cs.picture->tileMap->getTileIdxMap( currArea.lumaPos() );
#endif

  // Predict QP on start of quantization group
  if( pps.getUseDQP() && !cuCtx.isDQPCoded && CU::isQGStart( cu ) )
  {
    cuCtx.qp = CU::predictQP( cu, cuCtx.qp );
  }

  cu.qp          = cuCtx.qp;        //NOTE: CU QP can be changed by deltaQP signaling at TU level
  cu.chromaQpAdj = cs.chromaQpAdj;  //NOTE: CU chroma QP adjustment can be changed by adjustment signaling at TU level

  // coding unit

  bool isLastCtu = coding_unit( cu, partitioner, cuCtx );	//cu解码,返回isLastCtu表示是否为最后一个ctu的最后一个cu

  DTRACE( g_trace_ctx, D_QP, "x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Y().x, cu.Y().y, cu.Y().width, cu.Y().height, cu.qp );
  return isLastCtu;				//返回isLastCtu
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值