MENU

【HEVC学習】4. HMのTComPicYuvクラス

H.265/HEVC(High Efficiency Video Coding)のリファレンスソフトウェア(HM)のTComPicYuvクラスについて学んでいきます。前提として入力画像はQCIF(176×144)、YCbCr 4:2:0、configファイルはcfg/misc以下のencoder_randomaccess_main_GOP8.cfgを用いるものとして話を進めます。

目次

TComPicYuv::create()

TComPicYuv::create()では、createWithoutCUInfo()をコールして入力画像のバッファ領域を確保し、バッファ領域をCTU単位で分割した場合の各CTUへのアドレスオフセットm_ctuOffsetInBufferと、さらにCTUをサブブロック単位で分割した場合のアドレスオフセットm_subCuOffsetInBufferの値を生成します。

Void TComPicYuv::create ( const Int picWidth,                 ///< picture width
                          const Int picHeight,                ///< picture height
                          const ChromaFormat chromaFormatIDC, ///< chroma format
                          const UInt maxCUWidth,              ///< used for generating offsets to CUs.
                          const UInt maxCUHeight,             ///< used for generating offsets to CUs.
                          const UInt maxCUDepth,              ///< used for generating offsets to CUs.
                          const Bool bUseMargin)              ///< if true, then a margin of uiMaxCUWidth+16 and uiMaxCUHeight+16 is created around the image.

{
  createWithoutCUInfo(picWidth, picHeight, chromaFormatIDC, bUseMargin, maxCUWidth, maxCUHeight);

  // numCuInWidth=3(入力画像の幅176をmaxCUWidth(64)で割って切り上げ)
  const Int numCuInWidth  = m_picWidth  / maxCUWidth  + (m_picWidth  % maxCUWidth  != 0);
  // numCuInHeight=3(入力画像の高さ144をmaxCUHeight(64)で割って切り上げ)
  const Int numCuInHeight = m_picHeight / maxCUHeight + (m_picHeight % maxCUHeight != 0);
  for(Int chan=0; chan<MAX_NUM_CHANNEL_TYPE; chan++)
  {
    const ChannelType ch= ChannelType(chan);
    // Yの場合ctuHeight=64, CbCrの場合ctuHeight=32
    const Int ctuHeight = maxCUHeight>>getChannelTypeScaleY(ch);
    // Yの場合ctuWidth=64, CbCrの場合ctuWidth=32
    const Int ctuWidth  = maxCUWidth>>getChannelTypeScaleX(ch);
    // Yの場合stride=336, CbCrの場合stride=168(createWithoutCUInfo()の処理詳細を参照)
    const Int stride    = getStride(ch);

    // 各CTUへのアドレスオフセットを生成(3x3)
    m_ctuOffsetInBuffer[chan] = new Int[numCuInWidth * numCuInHeight];

    for (Int cuRow = 0; cuRow < numCuInHeight; cuRow++)
    {
      for (Int cuCol = 0; cuCol < numCuInWidth; cuCol++)
      {
        m_ctuOffsetInBuffer[chan][cuRow * numCuInWidth + cuCol] = stride * cuRow * ctuHeight + cuCol * ctuWidth;
      }
    }

    // 各サブブロックへのアドレスオフセットを生成(maxCUDepth=4の場合, 16x16)
    m_subCuOffsetInBuffer[chan] = new Int[(size_t)1 << (2 * maxCUDepth)];

    // maxCUDepth=4の場合, numSubBlockPartitions=16
    const Int numSubBlockPartitions=(1<<maxCUDepth);
    // minSubBlockHeight=4
    const Int minSubBlockHeight    =(ctuHeight >> maxCUDepth);
    // minSubBlockWidth=4
    const Int minSubBlockWidth     =(ctuWidth  >> maxCUDepth);

    for (Int buRow = 0; buRow < numSubBlockPartitions; buRow++)
    {
      for (Int buCol = 0; buCol < numSubBlockPartitions; buCol++)
      {
        m_subCuOffsetInBuffer[chan][(buRow << maxCUDepth) + buCol] = stride  * buRow * minSubBlockHeight + buCol * minSubBlockWidth;
      }
    }
  }
}

有効画像領域とCTU、生成されるm_ctuOffsetInBufferの値の関係を図にすると以下のようになります。CbCrの場合は有効画像領域、CTUの幅と高さがそれぞれ1/2になり、横方向と縦方向のアドレスオフセットの動き方もそれぞれ1/2になります。

TComPicYuv::createWithoutCUInfo()

TComPicYuv::createWithoutCUInfo()は入力画像に対してマージン込みのバッファ領域を各色成分ごとに確保します。マージン領域は有効画像領域の周囲に配置され、マージンの幅はmaxCUWidth+16、高さはmaxCUHeight+16です。

Void TComPicYuv::createWithoutCUInfo ( const Int picWidth,                 ///< picture width
                                       const Int picHeight,                ///< picture height
                                       const ChromaFormat chromaFormatIDC, ///< chroma format
                                       const Bool bUseMargin,              ///< if true, then a margin of uiMaxCUWidth+16 and uiMaxCUHeight+16 is created around the image.
                                       const UInt maxCUWidth,              ///< used for margin only
                                       const UInt maxCUHeight)             ///< used for margin only

{
  destroy();

  m_picWidth          = picWidth;
  m_picHeight         = picHeight;
  m_chromaFormatIDC   = chromaFormatIDC;
  m_marginX          = (bUseMargin?maxCUWidth:0) + 16;   // for 16-byte alignment
  m_marginY          = (bUseMargin?maxCUHeight:0) + 16;  // margin for 8-tap filter and infinite padding
  m_bIsBorderExtended = false;

  // assign the picture arrays and set up the ptr to the top left of the original picture
  for(UInt comp=0; comp<getNumberValidComponents(); comp++)
  {
    const ComponentID ch=ComponentID(comp);
    // マージン込みの入力画像のバッファ領域を確保
    m_apiPicBuf[comp] = (Pel*)xMalloc( Pel, getStride(ch) * getTotalHeight(ch));
    // 有効画像領域へのポインタを算出
    m_piPicOrg[comp]  = m_apiPicBuf[comp] + (m_marginY >> getComponentScaleY(ch)) * getStride(ch) + (m_marginX >> getComponentScaleX(ch));
  }
  // initialize pointers for unused components to NULL
  for(UInt comp=getNumberValidComponents();comp<MAX_NUM_COMPONENT; comp++)
  {
    m_apiPicBuf[comp] = NULL;
    m_piPicOrg[comp]  = NULL;
  }

  for(Int chan=0; chan<MAX_NUM_CHANNEL_TYPE; chan++)
  {
    m_ctuOffsetInBuffer[chan]   = NULL;
    m_subCuOffsetInBuffer[chan] = NULL;
  }
}

m_apiPicBufは確保したバッファ領域先頭へのポインタ、m_piPicOrgは有効画像領域先頭へのポインタになります。これらのポインタとマージン領域、有効画像領域の関係を図にすると以下のようになります。CbCrの場合はマージンや有効領域の幅と高さがそれぞれ1/2になります。

TComPicYuv::copyToPic()

TComPicYuv::copyToPic()では、pcPicYuvDst(コピー先)の示すバッファ領域に現バッファ領域を各色成分ごとにコピーします。

Void  TComPicYuv::copyToPic (TComPicYuv*  pcPicYuvDst) const
{
  assert( m_chromaFormatIDC == pcPicYuvDst->getChromaFormat() );

  for(Int comp=0; comp<getNumberValidComponents(); comp++)
  {
    const ComponentID compId=ComponentID(comp);
    const Int width     = getWidth(compId);
    const Int height    = getHeight(compId);
    const Int strideSrc = getStride(compId);
    assert(pcPicYuvDst->getWidth(compId) == width);
    assert(pcPicYuvDst->getHeight(compId) == height);

    // コピー元とコピー先のバッファ領域の幅(stride)が同じ場合
    if (strideSrc==pcPicYuvDst->getStride(compId))
    {
      // バッファの全領域をコピー
      // getBuf()によりm_apiPicBuf(バッファ領域先頭へのポインタ)が得られる
      ::memcpy ( pcPicYuvDst->getBuf(compId), getBuf(compId), sizeof(Pel)*strideSrc*getTotalHeight(compId));
    }
    else
    {
      // getAddr()によりm_piPicOrg(有効画像領域先頭へのポインタ)が得られる
      const Pel *pSrc       = getAddr(compId);
            Pel *pDest      = pcPicYuvDst->getAddr(compId);
      const UInt strideDest = pcPicYuvDst->getStride(compId);

      // コピー元とコピー先のバッファ領域の幅(stride)が異なる場合
      // コピー元の有効画像領域をコピー先の有効画像領域にコピー
      for(Int y=0; y<height; y++, pSrc+=strideSrc, pDest+=strideDest)
      {
        ::memcpy(pDest, pSrc, width*sizeof(Pel));
      }
    }
  }
}
  • URLをコピーしました!

この記事を書いた人

映像処理ハードウェアの研究開発をしています。ASIC, FPGA, 機械学習などの話題に興味があります。このブログでは、自分が最近勉強したことなどを中心にマイペースに発信していきます。

目次