本文整理自:1:http://blog.youkuaiyun.com/nb_vol_1/article/details/51145006
2:http://blog.youkuaiyun.com/NB_vol_1/article/details/71187426
首先感谢大神授权可以整理转载,后面会接着读大神的博客,所以后面一段时间里文章基本也是整理自这位大神。
1、TComDataCU。TComDataCU:LCU 及其子 CU 的数据结构,存储了一个 LCU 所有的相关信息,里面重要的数据结构包括:
m_uiCUAddr:一个 LCU 在 slice 中的位置
m_uiAbsIdxInLCU:当前 CU 在 LCU 中的位置,位置用 Z 扫描顺序
m_puhWidth: CU 的宽度
m_puhHeight:CU 的高度
m_puhDepth: CU 所处的深度
m_pePartSize : PU 的类型
m_pePredMode:编码模式
m_pcTrCoeffY,m_pcTrCoeffCb,m_pcTrCoeffCr:量化后的系数
m_puhLumaIntraDir:亮度的模式信息
m_puhChromaIntraDir:色度的模式信息
m_puhInterDir:帧间的预测方向
m_apiMVPIdx:MVP 索引
m_apiMVPNum:MVP 的候选数
以上的数据结构都是以动态存储来分配空间,一般只有一维,这一维具体取值的含义就是 CU 里面的每个对应的 4x4 的小块的信息,而开辟的数目就是 CU 所包含的 4x4 的数目,而在实际编码时也是编码了这些信息。
2、RDO 时所用到的主要临时变量:
2.1、m_ppcQTTempCoeffY, m_ppcQTTempCoeffCb, m_ppcQTTempCoeffCr: RQT时每层的量化系数,都保存在此,是为了确定最终分割后可以很容易的获取最优值
2.2、m_pcQTTempCoeffY,m_pcQTTempCoeffCb,m_pcQTTempCoeffCr:CU 层的量化系数暂存地,只有帧间编码时才会用到,是中间变量
2.3、m_pcQTTempTComYuv : 重建视频的暂存缓冲区
2.4、m_puhQTTempCbf: cbf 的暂存
2.4、m_puhQTTempTrIdx:变换层数的暂存
2.5、m_ppcBestCU:存储每层最优(RD代价最小)的CU的信息
2.6、m_ppcTempCU: 存储每层CU的信息的临时变量
2.7、m_ppcPredYuvBest: 存储每层最优的预测值
2.8、m_ppcResiYuvBest:存储每层最优的残差值
2.9、m_ppcRecoYuvBest:存储每层最优的重建值
2.10、m_ppcPredYuvTemp:存储每层预测值的临时变量
2.11、m_ppcResiYuvTemp:存储每层残差值的临时变量
2.12、m_ppcRecoYuvTemp:存储每层重建值的临时变量
2.13、m_ppcOrigYuv: :存储每层对应的原始值
3、yuv 的存储的关系
3.1、TComYuv 数据结构。由 m_apiBufY,m_apiBufU 以及 m_apiBufV 三个 buffer 组成,通用的 yuv数据结构,存储是 yuv 的亮度和色度信息
3.2、TComPicYuv 数据结构。图像层级的 yuv 数据结构,存储的是一帧的 yuv 信息,主要用于 ALF 和去方块滤波等处理的过程中
3.3、TComYuv 的类型的变量存储的是 RDO 时的值,最优的信息要存在 TComPicYuv中,便于输出和进行全局处理
下面的部分是和CU索引相关的,部分是和上面内容相关的。
1. 扫描顺序
HEVC中对像素块的扫描方式有两种:Raster和Zscan
Raster扫描方式:从上到下,从左到右进行扫描,是最直观也是最容易理解的方式
Zscan扫描方式:因为CU是递归划分的,因此为了方便处理,HM中使用了Z扫描,也就是从左上角按照Z字形扫描到右下角。下面是Zscan扫描的一个例子:
2.CU的划分
CU按照四叉树的方式自上而下,进行递归划分,由于使用了这种递归的方式,因此HEVC中必须要使用Zscan的扫描方式。每划分一次CU深度加一:
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
3.HEVC中对扫描顺序以及地址的处理
3.1 初始化扫描顺序
1、把每一个LCU都划分为4x4的小块,分别以Raster和Zscan的方式对4x4的小块进行编号(也就是索引)
2、把Raster到Zscan的编号映射存储在g_auiRasterToZscan数组中
3、把Zscan到Raster的编号映射存储在g_auiZscanToRaster数组中
4、把Raster索引到像素地址的映射放在g_auiRasterToPelX和g_auiRasterToPelY中
后面的代码为HM16.3版本,最新版本为16.9仅供参考:
在TEncCU.cpp找关于关于这几个函数的代码如下:
Void initZscanToRaster ( Int iMaxDepth, Int iDepth, UInt uiStartVal, UInt*& rpuiCurrIdx )
{
Int iStride = 1 << ( iMaxDepth - 1 );
if ( iDepth == iMaxDepth )
{
rpuiCurrIdx[0] = uiStartVal;
rpuiCurrIdx++;
}
else
{
Int iStep = iStride >> iDepth;
initZscanToRaster( iMaxDepth, iDepth+1, uiStartVal, rpuiCurrIdx );
initZscanToRaster( iMaxDepth, iDepth+1, uiStartVal+iStep, rpuiCurrIdx );
initZscanToRaster( iMaxDepth, iDepth+1, uiStartVal+iStep*iStride, rpuiCurrIdx );
initZscanToRaster( iMaxDepth, iDepth+1, uiStartVal+iStep*iStride+iStep, rpuiCurrIdx );
}
}
Void initRasterToZscan ( UInt uiMaxCUWidth, UInt uiMaxCUHeight, UInt uiMaxDepth )
{
UInt uiMinCUWidth = uiMaxCUWidth >> ( uiMaxDepth - 1 );
UInt uiMinCUHeight = uiMaxCUHeight >> ( uiMaxDepth - 1 );
UInt uiNumPartInWidth = (UInt)uiMaxCUWidth / uiMinCUWidth;
UInt uiNumPartInHeight = (UInt)uiMaxCUHeight / uiMinCUHeight;
for ( UInt i = 0; i < uiNumPartInWidth*uiNumPartInHeight; i++ )
{
g_auiRasterToZscan[ g_auiZscanToRaster[i] ] = i;
}
}
Void initRasterToPelXY ( UInt uiMaxCUWidth, UInt uiMaxCUHeight, UInt uiMaxDepth )
{
UInt i;
UInt* uiTempX = &g_auiRasterToPelX[0];
UInt* uiTempY = &g_auiRasterToPelY[0];
UInt uiMinCUWidth = uiMaxCUWidth >> ( uiMaxDepth - 1 );
UInt uiMinCUHeight = uiMaxCUHeight >> ( uiMaxDepth - 1 );
UInt uiNumPartInWidth = uiMaxCUWidth / uiMinCUWidth;
UInt uiNumPartInHeight = uiMaxCUHeight / uiMinCUHeight;
uiTempX[0] = 0; uiTempX++;
for ( i = 1; i < uiNumPartInWidth; i++ )
{
uiTempX[0] = uiTempX[-1] + uiMinCUWidth; uiTempX++;
}
for ( i = 1; i < uiNumPartInHeight; i++ )