VTM1.0代码阅读:coding_tree_unit和coding_tree函数

coding_tree_unit函数进行一个CTU的信息的编码,将CTU的信息编码为二进制码
coding_tree_unit函数在两个地方会被调用:
第一个地方是EncSlice::encodeCtus,这里调用coding_tree_unit仅仅是为了更新各个上下文模型的参数,并没有真的编码传输。
第二个地方是EncSlice::encodeSlice,这里是真正的编码端将CTU信息编码为二进制码流的地方。

入口参数cs是picture即当前帧的cs,该cs.area为整个picture的区域大小,cs中包含有当前帧所有CTU的所有信息。入口参数area即当前需要编码的CTU区域,ctuRsAddr为CTU的rs扫描地址。
EncSlice::encodeCtus中调用该函数时skipSao为true,需要进行SAO,因为encodeCtus函数进行的是CTU的编码,compress完一个CTU之后还需要对它的重建像素进行SAO,以便下一个CTU的帧内预测。
EncSlice::encodeSlice真正编码时的skipSao为false,无需SAO,这时只需要考虑编码就行。

void CABACWriter::coding_tree_unit( CodingStructure& cs, const UnitArea& area, int (&qps)[2], unsigned ctuRsAddr, bool skipSao /* = false */ )
{
     
  CUCtx cuCtx( qps[CH_L] );
  Partitioner *partitioner = PartitionerFactory::get( *cs.slice );

  partitioner->initCtu( area, CH_L, *cs.slice );	//partitioner用来记录CTU到各个cu的划分路径

  if( !skipSao )
  {
     
    sao( *cs.slice, ctuRsAddr );				//SAO
  }
  coding_tree( cs, *partitioner, cuCtx );		//CTU编码
  qps[CH_L] = cuCtx.qp;
  if( CS::isDualITree( cs ) && cs.pcv->chrFormat != CHROMA_400 )	//DualITree,I帧,coding_tree编码帧内色度模式的信息
  {
     
    CUCtx cuCtxChroma( qps[CH_C] );
    partitioner->initCtu( area, CH_C, *cs.slice );
    coding_tree( cs, *partitioner, cuCtxChroma );	//DualITree 色度域ctu编码
    qps[CH_C] = cuCtxChroma.qp;
  }

  delete partitioner;
}

coding_tree递归调用,按照ctu在xCompressCU中得到的划分树,寻址到ctu中的每个cu,对于每个cu,调用coding_unit函数进行编码。

void CABACWriter::coding_tree( const CodingStructure& cs, Partitioner& partitioner, CUCtx& cuCtx )
{
     
  const PPS      &pps         = *cs.pps;
  const UnitArea &currArea    = partitioner.currArea();		//当前递归到的处理区域
  const CodingUnit &cu        = *cs.getCU( currArea.blocks[partitioner.chType], partitioner.chType );
																	//get目前区域的左上角位置的cu
  // Reset delta QP coding flag and ChromaQPAdjustemt coding flag	//按照该cu中的depth信息,判断当前区域是否继续划分,如果不需要划分,就表示目前区域就是cu,直接调用coding_unit函数进行编码
  if( pps.getUseDQP() && partitioner.currDepth <= pps.getMaxCuDQPDepth() )	//如果按照cu中的depth信息依然需要划分,那么递归调用coding_tree划分当前区域,寻址cu
  {
     
    cuCtx.isDQPCoded          = false;
  }
  if( cs.slice->getUseChromaQpAdj() && partitioner.currDepth <= pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth() )
  {
     
    cuCtx.isChromaQpAdjCoded  = false;
  }

  const PartSplit implicitSplit = partitioner.getImplicitSplit( cs );

  // QT
  bool canQtSplit = partitioner.canSplit( CU_QUAD_SPLIT, cs );	//当前区域是否可以进行QT划分
																//长宽不等或者已经经过BT/TT划分,那么不能QT
  if( canQtSplit )
  {
     
    // split_cu_flag
    bool qtSplit = implicitSplit == CU_QUAD_SPLIT;

    if( !qtSplit && implicitSplit != CU_QUAD_SPLIT )
    {
     
      qtSplit = ( cu.qtDepth > partitioner.currQtDepth );		//根据cu中的depth信息,判断当前区域是否需要划分
      split_cu_flag( qtSplit, cs, partitioner );			//编码当前区域是否QT划分的flag
    }

    // quad-tree split
    if( qtSplit )
    {
     
      partitioner.splitCurrArea( CU_QUAD_SPLIT, cs );		//当前区域QT划分,4个子区域在partitioner中压栈

      do
      {
     
        if( cs.picture->blocks[partitioner.chType].contains( partitioner.currArea().blocks[partitioner.chType].pos() ) )
        {
     
          coding_tree( cs, partitioner, cuCtx );	//对每个qt子区域,递归调用coding_tree,寻址其中的cu
        }
      } while( partitioner.nextPart( cs ) );

      partitioner.exitCurrSplit();				//4个子区域在partitioner中出栈
      return;						//当前区域处理完毕,直接return
    }
  }

  {
     
     MT
    //bool mtSplit = partitioner.canSplit( CU_MT_SPLIT, cs );

    //if( mtSplit )
    // MT
    bool mtSplit = partitioner.canSplit( CU_MT_SPLIT, cs );		//当前区域是否可以进行BT/TT划分

    if( mtSplit )
    {
     
      const PartSplit splitMode = CU::getSplitAtDepth( cu, partitioner.currDepth );		//getSplitAtDepth函数获取当前currDepth时,cu会进行的MT划分方式

      CHECK( implicitSplit != CU_DONT_SPLIT && implicitSplit != splitMode, "Different split found than the implicit split" );

      if( implicitSplit == CU_DONT_SPLIT )	//implicitSplit只取值QT和dont_split,所以这里一般均为true
      {
     
        split_cu_mode_mt( splitMode, cs, partitioner );		//编码MT划分方式的信息
      }

      if( splitMode != CU_DONT_SPLIT )
      {
     
        partitioner.splitCurrArea( splitMode, cs );		//按照得到的mt划分方式,划分得到子区域
        do
        {
     
          if( cs.picture->blocks[partitioner.chType].contains( partitioner.currArea().blocks[partitioner.chType].pos() ) )
          {
     
            coding_tree( cs, partitioner, cuCtx );		//对每个mt子区域,递归调用coding_tree,寻址其中的cu
          }
        } while( partitioner.nextPart( cs ) );

        partitioner.exitCurrSplit();
        return;						//当前区域处理完毕,直接return
      }
    }
  }

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

  // coding unit
  coding_unit( cu, partitioner, cuCtx );		//当前区域既不QT也不MT划分,表示已经寻址到CTU划分树的叶子节点,此时即为cu区域,直接编码cu

  DTRACE_COND( ( isEncoding() ), 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 );
  DTRACE_BLOCK_REC_COND( ( !isEncoding() ), cs.picture->getRecoBuf( cu ), cu, cu.predMode );
}

你可能感兴趣的:(VTM1.0代码阅读:coding_tree_unit和coding_tree函数)