TComPicYuv、TComPic、TComPicSym、TComDataCU以及TComYuv的关系

转载TComPicYuv、TComPic、TComPicSym、TComDataCU以及TComYuv的关系https://blog.csdn.net/nb_vol_1/article/details/71173023

原帖写的已经很清楚了,纸上得来终觉浅,自己写一下熟悉一遍

 1、HM首先使用TComPicYuv保存从文件中读取出来的YUV数据
    2、利用TComPicYuv构造TComPic,并把YUV数据复制给他(TComPic包含了TComPicYuv成员)
    3、TComPic表示一帧图像,包含了数据(TComPicYuv),以及图像信息(TComPicSym,在TComPicSym中还包含了TComDataCU)
    4、TComPic被放入图像队列中
    5、处理图像队列中的每一个TComPic,实际是处理TComPic中的每一个CTU/CU(存放在TComPicSym中)
    6、从TComPic中取出每一个CTU(用TComDataCU表示),调用xCompressCU进行处理
    7、根据原始的CTU初始化TEncCu中的TComDataCU(最优的和临时的),这两个在编码的时候会使用
    8、把TComPic的数据复制给TComYuv对象,表示编码过程中的数据(原始,预测、残差、重建等)
    9、进行编码,tempCU用于编码过程中,bestCU用于保存最优信息
    10、产生的最优信息会被复制回TComPicSym中

最重要的TComPicYuv,TComPic,TComPicSym三个类,一个个来看

TComPicYuv

class TComPicYuv
{
private:

Pel*  m_apiPicBuf[MAX_NUM_COMPONENT];    //MAX_NUM_COMPONENT=3,三个颜色分量的缓冲区,包括了填充的内容 

  // 三个颜色分量的起始地址,由m_apiPicBufY计算得到  
  Pel*  m_piPicOrg[MAX_NUM_COMPONENT];              ///< m_apiPicBufY + m_iMarginLumaY*getStride() + m_iMarginLumaX

  Int   m_picWidth;                                 ///< Width of picture in pixels
  Int   m_picHeight;                                ///< Height of picture in pixels
  ChromaFormat m_chromaFormatIDC;                   ///< Chroma Format,色度采样率
  Int*  m_ctuOffsetInBuffer[MAX_NUM_CHANNEL_TYPE];  ///< Gives an offset in the buffer for a given CTU (and channel)
  Int*  m_subCuOffsetInBuffer[MAX_NUM_CHANNEL_TYPE];///< Gives an offset in the buffer for a given sub-CU (and channel), relative to start of CTU


 Int   m_marginX;      //X分量的填充宽度和高度  < margin of Luma channel (chroma's may be smaller, depending on ratio)
  Int   m_marginY;      //Y分量的填充宽度和高度                      ///< margin of Luma channel (chroma's may be smaller, depending on ratio)

  Bool  m_bIsBorderExtended;// 是否需要填充图像  

}

这就是一个图像类,保存了一些图像高度和宽度,还有YUV数据,可以看到有MarginX MarginY两个填充变量,关于填充的理解参考

HEVC MarginX MarginY的理解https://blog.csdn.net/rankling315/article/details/54707841

有点类似于回字,内部框是原始图像,外部框和内部框之间的是填充

m_apiPicBu就是回字

m_piPicOrg就是内部真实为填充的图像YUV的起始位置

Int*  m_ctuOffsetInBuffer[MAX_NUM_CHANNEL_TYPE];  ///< Gives an offset in the buffer for a given CTU (and channel)
  Int*  m_subCuOffsetInBuffer[MAX_NUM_CHANNEL_TYPE];///< Gives an offset in the buffer for a given sub-CU (and channel), relative to start of CTU

这两个变量

m_ctuOffsetInBuffer就是该帧中每个LCU的起始位置(偏移量)

m_subCuOffsetInBuffer子就是子CU的起始位置(偏移量)

但是是指相对于m_apiPicBuf指针的偏移量,也就是填充后的图像偏移量,LCU是从填充后开始算的,这点我是没想到

其中m_chromaFormatIDC是色度采样率,是一个枚举类型

enum ChromaFormat
{
  CHROMA_400        = 0,
  CHROMA_420        = 1,
  CHROMA_422        = 2,
  CHROMA_444        = 3,
  NUM_CHROMA_FORMAT = 4
};

同理ChannelType是通道,亮度和色度

enum ChannelType
{
  CHANNEL_TYPE_LUMA    = 0,
  CHANNEL_TYPE_CHROMA  = 1,
  MAX_NUM_CHANNEL_TYPE = 2
};

ComponentID是YCbCr

enum ComponentID
{
  COMPONENT_Y       = 0,
  COMPONENT_Cb      = 1,
  COMPONENT_Cr      = 2,
  MAX_NUM_COMPONENT = 3
};

再看看cerate函数,有两个,一个是带CUInfo的,一个不带

create函数第一行就调用了createWithoutCUInfo,所以先来看看createWithoutCUInfo函数

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
  //分配图片矩阵并将 PTR 设置为原始图片的左上角
  //getNumberValidComponents函数,400采样返回1,不是400采样返回3
  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;
  }
}

其中getNumberValidComponents函数,YUV400采样返回1,不是400采样返回3

  UInt          getNumberValidComponents() const { return ::getNumberValidComponents(m_chromaFormatIDC); }

static inline UInt        getNumberValidComponents  (const ChromaFormat fmt)                       { return (fmt==CHROMA_400) ? 1 : MAX_NUM_COMPONENT;                  }

以YUV420为例,comp循环从0到2,对应的ch为Y,Cb,Cr,即一个亮度两个色度,之所以这么搞是因为三个缓存区的大小因为采样率的关系并不一致,对于亮度就是填充后的宽x高,但对于色度4:2:0的两个色度的宽高都是亮度的一半。

  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));

// 对比一下这个公式m_apiPicBufY + m_iMarginLumaY*getStride() + m_iMarginLumaX
    //可以理解为 (m_marginY >> getComponentScaleY(ch)) 就是m_iMarginLumaY, (m_marginX >> getComponentScaleX(ch))就是m_iMarginLumaX

看看 getStride(ch)和getTotalHeight(ch),getStride和getTotalHeight就是填充后的长度和宽度

 Int           getStride         (const ComponentID id) const
 { return ((m_picWidth     ) + (m_marginX  <<1)) >> getComponentScaleX(id); }
  Int           getTotalHeight    (const ComponentID id) const { return ((m_picHeight    ) + (m_marginY  <<1)) >> getComponentScaleY(id); }
static inline UInt        getChannelTypeScaleX      (const ChannelType id, const ChromaFormat fmt) { return (isLuma(id) || (fmt==CHROMA_444)) ? 0 : 1;                  }
static inline UInt        getChannelTypeScaleY      (const ChannelType id, const ChromaFormat fmt) { return (isLuma(id) || (fmt!=CHROMA_420)) ? 0 : 1;                  }

getComponentScaleX(ch),ch是通道的类型,是luna亮度或者该图像的格式为444则返回0,即不对变长进行缩放。否则要进行右移1位,也就是缩小1倍.
  getComponentScaleY(ch)同理,如果是luna亮度或者该图像的格式不是420则返回0,即不对变长进行缩放。否则进行右移1位也就是缩小1倍,因为420格式下的cb和cr是Y的宽高的1/2.

可以看到只要是亮度,则不缩小。

在4:2:0的采样中,色度的宽高都是亮度的一半,所以缩放。

在4:4:4采样中,色度的宽高和亮度一致

在4:2:2采样中,色度的宽度缩小一半,高度保持不变

可以看到分配的空间是大于实际像素数量的

再来看看cerate函数,这个create主要是计算所有CTU左上角第一个像素在填充图像中三个分量的偏移位置(相对于m_apiPicBuf指针的偏移量,也就是填充后的图像偏移量),以及sub每一个4*4的子块相对于CTU的偏移量的偏移,代码逻辑挺好懂的就不赘述了

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);

//计算LCU的个数
  const Int numCuInWidth  = m_picWidth  / maxCUWidth  + (m_picWidth  % maxCUWidth  != 0);
  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);//Luma=0 ,Choma=1 Max=2
    const Int ctuHeight = maxCUHeight>>getChannelTypeScaleY(ch);
    const Int ctuWidth  = maxCUWidth>>getChannelTypeScaleX(ch);
    const Int stride    = getStride(ch);

    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;
      }
    }

    m_subCuOffsetInBuffer[chan] = new Int[(size_t)1 << (2 * maxCUDepth)];

    const Int numSubBlockPartitions=(1<<maxCUDepth);
    const Int minSubBlockHeight    =(ctuHeight >> maxCUDepth);
    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;
      }
    }
  }
}

还有一些成员函数蛮重要的

Int           getWidth          (const ComponentID id) const { return  m_picWidth >> getComponentScaleX(id);   }
  Int           getHeight         (const ComponentID id) const { return  m_picHeight >> getComponentScaleY(id);  }
  Int           getStride         (const ComponentID id) const { return ((m_picWidth     ) + (m_marginX  <<1)) >> getComponentScaleX(id); }
Int           getTotalHeight    (const ComponentID id) const { return ((m_picHeight    ) + (m_marginY  <<1)) >> getComponentScaleY(id); }
Pel*          getBuf            (const ComponentID ch)       { return  m_apiPicBuf[ch];   }
Pel*          getAddr           (const ComponentID ch)       { return  m_piPicOrg[ch];   }
Pel*          getAddr           (const ComponentID ch, const Int ctuRSAddr )       { return m_piPicOrg[ch] + m_ctuOffsetInBuffer[ch==0?0:1][ ctuRSAddr ]; }
 Pel*          getAddr           (const ComponentID ch, const Int ctuRSAddr, const Int uiAbsZorderIdx )
                                     { return m_piPicOrg[ch] + m_ctuOffsetInBuffer[ch==0?0:1][ctuRSAddr] + m_subCuOffsetInBuffer[ch==0?0:1][g_auiZscanToRaster[uiAbsZorderIdx]]; 

getWidth和getHeight获取帧的宽高

getStride和getTotalHeight获取图像填充后的宽高

getBuf是获取通道ch的缓存区首地址

getAddr是获取通道ch的实际图像首地址

 getAddr  (const ComponentID ch, const Int ctuRSAddr ) 是访问特定编码单元 (CU) 或分区单元 (PU) 的原始图片起始位置

TComPic

/// picture class (symbol + YUV buffers)

//TComPic表示一张图像,它包含YUV数据(TComPicYuv)和信息(TComPicSym)

class TComPic
{
public:
  typedef enum { PIC_YUV_ORG=0, PIC_YUV_REC=1, PIC_YUV_TRUE_ORG=2, NUM_PIC_YUV=3 } PIC_YUV_T;
     // TRUE_ORG is the input file without any pre-encoder colour space conversion (but with possible bit depth increment)
  TComPicYuv*   getPicYuvTrueOrg()        { return  m_apcPicYuv[PIC_YUV_TRUE_ORG]; }

private:
  UInt                  m_uiTLayer;               //  Temporal layer 时间层
  Bool                  m_bUsedByCurr;            //  Used by current picture  是否被作为参考帧  
  Bool                  m_bIsLongTerm;            //  IS long term picture 是否为长参考图像  
  TComPicSym            m_picSym;                 //  Symbol 图像的信息
  TComPicYuv*           m_apcPicYuv[NUM_PIC_YUV];  // 图像的数据(索引0是原始图像,索引1是重建图像)  

  TComPicYuv*           m_pcPicYuvPred;           //  Prediction // 图像的预测数据  
  TComPicYuv*           m_pcPicYuvResi;           //  Residual  图像的残差数据  
  Bool                  m_bReconstructed;          // 是否被重建  
  Bool                  m_bNeededForOutput;       // 是否需要输出 
  UInt                  m_uiCurrSliceIdx;         // Index of current slice  在此图像中,当前条带的索引 
  Bool                  m_bCheckLTMSB; 

  Bool                  m_isTop;                    // 顶场还是底场 
  Bool                  m_isField;                  // 帧还是场
 // 条带的CU链表,即:  
    // 每一个slice中有若干LCU,每一个LCU又被细分为各个CU  
    // std::vector<TComDataCU*>就是存放LCU的CU  
    // 没有用到,因为CTU的相关信息存放在TComPicSym中  
  std::vector<std::vector<TComDataCU*> > m_vSliceCUDataLink;
}

还有一个函数我经常使用:getPOC()

Int           getPOC() const                     { return  m_picSym.getSlice(m_uiCurrSliceIdx)->getPOC();  }

获取当前图像在视频序列中POC

可以看到成员属性有TComPicYuv和TComPicSym两个类成员

TComPicSym

//picture symbol class
//TComPicSym表示图像的信息,它内部有一个TComDataCU数组,描述了图像组每一个LCU(CTU)的信息

class TComPicSym
{
private:
  UInt          m_frameWidthInCtus; // 图像在横向上有多少个LCU
  UInt          m_frameHeightInCtus;// 图像在纵向上有多少个LCU 

  UInt          m_uiMinCUWidth; // 最小的CU的尺寸:4x4
  UInt          m_uiMinCUHeight;

  UChar         m_uhTotalDepth;       ///< max. depth LCU可以划分的最大深度:4  
  UInt          m_numPartitionsInCtu;  // = 1<<(m_uhTotalDepth<<1);256,我的理解是一个LCU有多少个最小的CU
  UInt          m_numPartInCtuWidth; //一个LCU横向上有多少个最小的CU
  UInt          m_numPartInCtuHeight;//一个LCU纵向上有多少个最小的CU
  UInt          m_numCtusInFrame; //=m_frameWidthInCtus* m_frameHeightInCtus

  std::deque<TComSlice*> m_apSlices;
 TComDataCU**  m_pictureCtuArray;   //其实是一维数组,数组大小为LCU数量,数组放的TComDataCU*
std::vector<TComTile> m_tileParameters;
//对于给定的TS地址转为Rs地址,该参数加上TS地址解地址就是得到的RS地址,这个参数也是一个数组,数组长度为该帧的LCU个数+1,在create函数中该数组初始化为LCU的序号索引,从0开始
UInt*         m_ctuTsToRsAddrMap;  

//TS地址转为Rs地址的映射,通过改参数计算当前Tile序号currentTileIdx
UInt*         m_puiTileIdxMap; 
//对于给定的RS地址转为TS地址,该参数加上RS地址就是得到的TS地址,这个参数也是一个数组,数组长度为该帧的LCU个数+1
UInt*         m_ctuRsToTsAddrMap;

 TComSPS       m_sps;
 TComPPS       m_pps;

}

来看该类的create函数

Void TComPicSym::create  ( const TComSPS &sps, const TComPPS &pps, UInt uiMaxDepth )
{
  destroy();

  m_sps = sps;
  m_pps = pps;

  const ChromaFormat chromaFormatIDC = sps.getChromaFormatIdc();
  const Int iPicWidth      = sps.getPicWidthInLumaSamples();
  const Int iPicHeight     = sps.getPicHeightInLumaSamples();
  const UInt uiMaxCuWidth  = sps.getMaxCUWidth();
  const UInt uiMaxCuHeight = sps.getMaxCUHeight();

  m_uhTotalDepth       = uiMaxDepth;
  m_numPartitionsInCtu = 1<<(m_uhTotalDepth<<1); 1<<(4<<1) =256

  m_uiMinCUWidth       = uiMaxCuWidth  >> m_uhTotalDepth; 64>>4
  m_uiMinCUHeight      = uiMaxCuHeight >> m_uhTotalDepth;

  m_numPartInCtuWidth  = uiMaxCuWidth  / m_uiMinCUWidth;  // equivalent to 1<<m_uhTotalDepth
  m_numPartInCtuHeight = uiMaxCuHeight / m_uiMinCUHeight; // equivalent to 1<<m_uhTotalDepth

  m_frameWidthInCtus   = ( iPicWidth %uiMaxCuWidth  ) ? iPicWidth /uiMaxCuWidth  + 1 : iPicWidth /uiMaxCuWidth;
  m_frameHeightInCtus  = ( iPicHeight%uiMaxCuHeight ) ? iPicHeight/uiMaxCuHeight + 1 : iPicHeight/uiMaxCuHeight;

  m_numCtusInFrame     = m_frameWidthInCtus * m_frameHeightInCtus;
  m_pictureCtuArray    = new TComDataCU*[m_numCtusInFrame];

  clearSliceBuffer();
  allocateNewSlice();
//初始化m_pictureCtuArray数组,再调用TComDataCU::create函数,创建该该帧所有LCU的Data对象
for (UInt i=0; i<m_numCtusInFrame ; i++ )
  {
    m_pictureCtuArray[i] = new TComDataCU;
    m_pictureCtuArray[i]->create( chromaFormatIDC, m_numPartitionsInCtu, uiMaxCuWidth, uiMaxCuHeight, false, uiMaxCuWidth >> m_uhTotalDepth
#if ADAPTIVE_QP_SELECTION
      , m_pParentARLBuffer
#endif
      );
  }
}

  Void               setCtuTsToRsAddrMap( Int ctuTsAddr, Int ctuRsAddr )   { *(m_ctuTsToRsAddrMap + ctuTsAddr) = ctuRsAddr; }
  Void               setCtuRsToTsAddrMap( Int ctuRsAddr, Int ctuTsOrder )  { *(m_ctuRsToTsAddrMap + ctuRsAddr) = ctuTsOrder; }
UInt               getTileIdxMap( Int ctuRsAddr ) const                  { return *(m_puiTileIdxMap + ctuRsAddr); }
TComDataCU*        getCtu( UInt ctuRsAddr )                              { return m_pictureCtuArray[ctuRsAddr];  }

四个后面会经常用到的函数,Ts地址和Rs地址的相互转换

获取Tileindex

还有通过ctuRsAddr,获取 TComDataCU,ctuRsAddr就是LCU位置索引,按Rs顺序

TComDataCU

参考自:CU划分https://blog.csdn.net/Dillon2015/article/details/104584475?ops_request_misc=&request_id=&biz_id=102&utm_term=m_puhDepth&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-104584475.142%5Ev67%5Ewechat,201%5Ev3%5Econtrol_2,213%5Ev2%5Et3_esquery_v3&spm=1018.2226.3001.4187

CTU可以按四叉树方式递归划分为子CU,子CU按zig-zag顺序递归处理,如下图所示:

图中数字是CU的处理顺序。

实际上整个CTU的信息都在TComDataCU类中,且数据仅在CTU中存储一份,子CU只需指定其在CTU中的位置即可。类中成员变量m_absZIdxInCtu表示子CU在CTU中的位置(zig-zag顺序)。

CTU并不是按照二维数组形式表示的,因为CTU的划分方式不同,每个部分的深度也不同,所以CTU的“形状”不同无法用统一的形式表示出来。

为了使用统一的数据结构简洁的表示CTU,TComDataCU类将CTU划分成4x4的小块,存储每个小块的深度。64x64的CTU可以划分为256个4x4块,32x32的CTU可以划分为64个4x4块。

上图是一个32x32的CTU划分成64个4x4块的结果,按zig-zag顺序扫描。

TComDataCU类中成员m_puhWidth、m_puhHeight、m_puhDepth三个数组存储了每个4x4小块的宽、高和深度。

下面是一个真实的64x64CTU划分示例:

 

上面64x64的CTU包含256个4x4的块,每个块对应的宽、高、深如下:

   1、深度等于0时,CU的尺寸是64x64,该CU下面4x4小块的数量是256
   2、深度等于1时,CU的尺寸是32x32,该CU下面4x4小块的数量是64
   3、深度等于2时,CU的尺寸是16x16,该CU下面4x4小块的数量是16
   4、深度等于3时,CU的尺寸是8x8,该CU下面4x4小块的数量是4
 

由上图可以看到虽然不是每个CU都以最小4X4划分,但是 m_puhWidth、m_puhHeight、m_puhDepth记录的是最小4X4所处CU位置的深度,宽度和高度来确定的。并且是以Z字扫描的方式,10是因为这里是16进制,16进制10=十进制16

 因为四叉树划分都是正方形块,所以宽高一样。对比m_puhDepth数组和上面的划分结构可以看出两者结果一致。

HM中提供了两个数组g_auiZscanToRaster和g_auiRasterToZscan可以进行zig-zag顺序和光栅扫描顺序间的转换。

  1. g_auiZscanToRaster[ z-scan index ] = raster scan index

  2. g_auiRasterToZscan[ raster index ] = z-scan index

class TComDataCU
{
private:

  TComPic*      m_pcPic;    ///< picture class pointer // CU所在的图像 
  TComSlice*    m_pcSlice;  ///< slice header pointer   CU所在的条带  

  UInt          m_ctuRsAddr;                            ///< CTU (also known as LCU) address in a slice (Raster-scan address, as opposed to tile-scan/encoding order). CTU(LCU)地址,Raster-scan 扫描
  UInt          m_absZIdxInCtu;                         ///< absolute address in a CTU. It's Z scan order 在LCU中Z扫描顺序的地址
  UInt          m_uiCUPelX;                             ///< CU position in a pixel (X)  以像素为单位x方向的CU地址
  UInt          m_uiCUPelY;                             ///< CU position in a pixel (Y) 以像素为单位y方向的CU地址
  UInt          m_uiNumPartition;                       ///< total number of minimum partitions in a CU  当前CU中有多少个4x4的块
  UChar*        m_puhWidth;                    ///< array of widths 存放CU高度的数组
  UChar*        m_puhHeight;                   ///< array of heights  存放CU高度的数组
  UChar*        m_puhDepth;                    ///< array of depths  存放CU深度的数组  
  Int           m_unitSize;                  ///< size of a "minimum partition"  partition的最小尺寸,我理解为4X4

  TComDataCU*   m_pCtuAboveLeft;                        ///< pointer of above-left CTU. 当前CU左上角的CU  
  TComDataCU*   m_pCtuAboveRight;                       ///< pointer of above-right CTU.  当前CU右上角的CU  
  TComDataCU*   m_pCtuAbove;                            ///< pointer of above CTU.   当前CU上面的CU  
  TComDataCU*   m_pCtuLeft;                             ///< pointer of left CTU  当前CU左边的CU  
  TComDataCU*   m_apcCUColocated[NUM_REF_PIC_LIST_01];  ///< pointer of temporally colocated CU's for both directions 
  TComMvField   m_cMvFieldA;                            ///< motion vector of position A
  TComMvField   m_cMvFieldB;                            ///< motion vector of position B
  TComMvField   m_cMvFieldC;                            ///< motion vector of position C
  TComMv        m_cMvPred;                              ///< motion vector predictor
  Bool          m_bDecSubCu;                            ///< indicates decoder-mode
  Double        m_dTotalCost;                           ///< sum of partition RD costs  总的代价 
  Distortion    m_uiTotalDistortion;                    ///< sum of partition distortion  总的失真  
  UInt          m_uiTotalBits;                          ///< sum of partition bits  总的比特数  
  UInt          m_uiTotalBins;                          ///< sum of partition bins   总的二进制数  
  SChar         m_codedQP;  //量化的编码  

}

  Bool*         m_skipFlag;                             ///< skip
  SChar*        m_pePartSize;                           ///< 当前CU的分割尺寸
  SChar*        m_pePredMode;                           ///< 预测模式
 

 

函数重点就是四个, // create / destroy / initialize / copy

Void          create                        ( ChromaFormat chromaFormatIDC, UInt uiNumPartition, UInt uiWidth, UInt uiHeight, Bool bDecSubCu, Int unitSize);
Void          destroy                       ( );
Void          initCtu                       ( TComPic* pcPic, UInt ctuRsAddr );
Void          copySubCU                     ( TComDataCU* pcCU, UInt uiPartUnitIdx );
Void          copyInterPredInfoFrom         ( TComDataCU* pcCU, UInt uiAbsPartIdx, RefPicList eRefPicList );
Void          copyPartFrom                  ( TComDataCU* pcCU, UInt uiPartUnitIdx, UInt uiDepth );
Void          copyToPic                     ( UChar uiDepth );

create函数

这个函数是在TComPicSym::create函数中进行调用的

 for (UInt i=0; i<m_numCtusInFrame ; i++ )
  {
    m_pictureCtuArray[i] = new TComDataCU;
    m_pictureCtuArray[i]->create( chromaFormatIDC, m_numPartitionsInCtu, uiMaxCuWidth, uiMaxCuHeight, false, uiMaxCuWidth >> m_uhTotalDepth
#if ADAPTIVE_QP_SELECTION
      , m_pParentARLBuffer
#endif
      );
  }

传参m_numPartitionsInCtu 是该CU的4X4块数量,m_numPartitionsInCtu不一定就是256,LCU时是256,但如果是四叉树划分后深度不为0,就不是256了。

Void TComDataCU::create( ChromaFormat chromaFormatIDC, UInt uiNumPartition, UInt uiWidth, UInt uiHeight, Bool bDecSubCu, Int unitSize
#if ADAPTIVE_QP_SELECTION
                        , TCoeff *pParentARLBuffer
#endif
                        )
{
  m_bDecSubCu = bDecSubCu;

  m_pcPic              = NULL;
  m_pcSlice            = NULL;
  m_uiNumPartition     = uiNumPartition;
  m_unitSize = unitSize;

  if ( !bDecSubCu )
  {
    m_phQP               = (SChar*    )xMalloc(SChar,    uiNumPartition);
    m_puhDepth           = (UChar*    )xMalloc(UChar,    uiNumPartition);
    m_puhWidth           = (UChar*    )xMalloc(UChar,    uiNumPartition);
    m_puhHeight          = (UChar*    )xMalloc(UChar,    uiNumPartition);
  }

  m_pCtuAboveLeft      = NULL;
  m_pCtuAboveRight     = NULL;
  m_pCtuAbove          = NULL;
  m_pCtuLeft           = NULL;

  for(UInt i=0; i<NUM_REF_PIC_LIST_01; i++)
  {
    m_apcCUColocated[i]  = NULL;
  }
}

 

对深度,宽度,高度分配了m_numPartitionsInCtu个空间,然后对参考帧进行了初始化。

m_numPartitionsInCtu得具体情况具体分析,如果是上述TComPicSym::create函数中进行调用的的create,是以LCU调用的,如果是四叉树划分后的CU,就不是了。

m_ppcBestCU[i] = new TComDataCU; m_ppcBestCU[i]->create( chromaFormat, uiNumPartitions, uiWidth, uiHeight, false, uiMaxWidth >> (m_uhTotalDepth - 1) );
m_ppcTempCU[i] = new TComDataCU; m_ppcTempCU[i]->create( chromaFormat, uiNumPartitions, uiWidth, uiHeight, false, uiMaxWidth >> (m_uhTotalDepth - 1) );

init函数

/**
 Initialize top-level CU: create internal buffers and set initial values before encoding the CTU.
 
 \param  pcPic       picture (TComPic) class pointer
 \param  ctuRsAddr   CTU address in raster scan order
 */

翻译一下就是初始化最顶层CU(LCU),在对 CTU 进行编码之前创建内部缓冲区并设置初始值。

换句话说就是这个函数只会被LCU层调用,普通CU的初始化使用initSubCU ,其中sub是指子。

//主要流程:
//(1)计算当前LCU在图像中的像素地址
//(2)计算LCU可以被分成多少个4x4的CU
//(3)把片的起始CU的地址存储起来
//(4)设置每一个4x4子CU的参数信息
//(5)设置当前LCU的左边、上方、右上角、左上角的相邻的LCU
//(6)设置参考帧的数组
 

 Void TComDataCU::initCtu( TComPic* pcPic, UInt ctuRsAddr ){
const UInt maxCUWidth = pcPic->getPicSym()->getSPS().getMaxCUWidth();
  const UInt maxCUHeight= pcPic->getPicSym()->getSPS().getMaxCUHeight();
  m_pcPic              = pcPic; // 当前CU所属的图像
  m_pcSlice            = pcPic->getSlice(pcPic->getCurrSliceIdx());  // 当前CU所属的片
  m_ctuRsAddr          = ctuRsAddr;   // 当前CU地址
   LCU在图像中的实际像素地址
  m_uiCUPelX           = ( ctuRsAddr % pcPic->getFrameWidthInCtus() ) * maxCUWidth; 
  m_uiCUPelY           = ( ctuRsAddr / pcPic->getFrameWidthInCtus() ) * maxCUHeight;
  m_absZIdxInCtu       = 0;
  m_dTotalCost         = MAX_DOUBLE; // 总的RD消耗
  m_uiTotalDistortion  = 0; 
  m_uiTotalBits        = 0;
  m_uiTotalBins        = 0;
  m_uiNumPartition     = pcPic->getNumPartitionsInCtu(); //256,当前LCU中可以有多少个最小4X4的CU


后面是一些初始化操作
 memset( m_puhWidth          , maxCUWidth,                 m_uiNumPartition * sizeof( *m_puhWidth ) );
  memset( m_puhHeight         , maxCUHeight,                m_uiNumPartition * sizeof( *m_puhHeight ) );
// Setting neighbor CU 设置相邻CU 
  m_pCtuLeft        = NULL;
  m_pCtuAbove       = NULL;
  m_pCtuAboveLeft   = NULL;
  m_pCtuAboveRight  = NULL;
 UInt frameWidthInCtus = pcPic->getFrameWidthInCtus(); // 图像在横向上有多少个CTU

  // 分别获取当前CU各个方向上相邻的CU
  if ( m_ctuRsAddr % frameWidthInCtus )//说明不是每行第一个LCU,则左边一定有
  {
    m_pCtuLeft = pcPic->getCtu( m_ctuRsAddr - 1 );
  }

  if ( m_ctuRsAddr / frameWidthInCtus )//m_ctuRsAddr>frameWidthInCtus ,结果!=0,说明不是第一行,则上边一定有
  {
    m_pCtuAbove = pcPic->getCtu( m_ctuRsAddr - frameWidthInCtus );
  }

  if ( m_pCtuLeft && m_pCtuAbove ) //左边和上边都有,则左上角一定有
  {
    m_pCtuAboveLeft = pcPic->getCtu( m_ctuRsAddr - frameWidthInCtus - 1 );
  }

  if ( m_pCtuAbove && ( (m_ctuRsAddr%frameWidthInCtus) < (frameWidthInCtus-1) )  ) //右上角
  {
    m_pCtuAboveRight = pcPic->getCtu( m_ctuRsAddr - frameWidthInCtus + 1 );
  }
//设置参考帧的数组
  for(UInt i=0; i<NUM_REF_PIC_LIST_01; i++)
  {
    const RefPicList rpl=RefPicList(i);
    if ( getSlice()->getNumRefIdx( rpl ) > 0 )
    {
      m_apcCUColocated[rpl] = getSlice()->getRefPic( rpl, 0)->getCtu( m_ctuRsAddr );
    }
  }

}

Init时还未编码,memset宽度和高度为最大尺寸及64

TComYuv

general YUV buffer class

  TComYuv保存了每一个CU对应的YUV数据,原始数据从TComPic中得到(实际从TComPic的TComPicYuv成员中得到)。它主要用于表示编码过程中的原始数据、预测数据、残差数据以及重建数据

class TComYuv
{
private:
  //  YUV buffer

  Pel*    m_apiBuf[MAX_NUM_COMPONENT]; // 颜色分量的起始地址 

  //  Parameter for general YUV buffer usage
 
  UInt     m_iWidth; // CU(可以是LCU,也可以是普通CU)的宽和高  
  UInt     m_iHeight;
  ChromaFormat m_chromaFormatIDC; < Chroma Format
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值