千呼万唤始出来,VTM5.0的rc1版本终于发布了。
基于前面VTM4.0中的内容,所有帧内预测的函数都已学习过。今天我们除了看一下VTM5.0中帧内预测的改变,也来关注一下跟C++更相关的东西:IntraPrediction类的定义。
总体来说,除了新增加了几个帧内的技术,还添加了一个IntraPredParam类,用于存放帧内预测过程中的一些重要参数。所以新版本中,函数的参数数量有了一定的减少,也是代码更加精简的一种标志。
在C++中private中主要定义的是数据。这里我们可以看到,IntraPrediction类中的private里存放的都是像素缓存值。
private:
Pel* m_piYuvExt[MAX_NUM_COMPONENT][NUM_PRED_BUF];//存预测值的位置,这个buffer存在三个通道的和是否滤波的
int m_iYuvExtSize;//预测值的长度
Pel* m_yuvExt2[MAX_NUM_COMPONENT][4];
int m_yuvExtSize2;
static const uint8_t m_aucIntraFilter[MAX_NUM_CHANNEL_TYPE][MAX_INTRA_FILTER_DEPTHS];//滤波强度?
Pel* m_piTemp;//预测值缓存的临时存储位置
Pel* m_pMdlmTemp; // for MDLM mode MDLM预测值缓存的临时存储位置
private里,VTM5.0新增了如下两部分代码:1、帧内预测参数,2、ALWIP矩阵帧内预测(利用深度学习的方法训练得到一组矩阵参数,用他们进行预测)
struct IntraPredParam //parameters of Intra Prediction
{
bool refFilterFlag; //是否使用滤波器
bool applyPDPC; //是否使用PDPC
bool isModeVer; //是否是垂直类型模式(模式号为35-66)
int multiRefIndex; //多参考行索引
int whRatio; //宽高比
int hwRatio; //高宽比
int intraPredAngle;//帧内预测角度
int invAngle; //预测角度
bool interpolationFlag;
IntraPredParam() : //构造函数,将IntraPredParam的对象进行默认初始化
refFilterFlag ( false ),
applyPDPC ( false ),
isModeVer ( false ),
multiRefIndex ( -1 ),
whRatio ( 0 ),
hwRatio ( 0 ),
intraPredAngle ( std::numeric_limits<int>::max() ),
invAngle ( std::numeric_limits<int>::max() ),
interpolationFlag ( false ) {}
};
IntraPredParam m_ipaParam;//上面这个IntraPredParam类的对象
#if JVET_N0217_MATRIX_INTRAPRED
MatrixIntraPrediction m_matrixIntraPred;//ALWIP
#endif
protected对于子类来说是public的,没有任何限制。而对于其他的外部class,protected就变成private。
protected:
ChromaFormat m_currChromaFormat;//色彩格式
int m_topRefLength;//上边长度
int m_leftRefLength;//左边长度
// prediction
void xPredIntraPlanar ( const CPelBuf &pSrc, PelBuf &pDst, const SPS& sps );//PLANAR预测
void xPredIntraDc ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const bool enableBoundaryFilter = true );//DC预测
#if HEVC_USE_HOR_VER_PREDFILTERING
void xPredIntraAng ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const uint32_t dirMode, const ClpRng& clpRng, const bool bEnableEdgeFilters, const SPS& sps
, int multiRefIdx
, const bool enableBoundaryFilter = true );
#else
#if JVET_M0102_INTRA_SUBPARTITIONS
void xPredIntraAng ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const uint32_t dirMode, const ClpRng& clpRng, const SPS& sps,
int multiRefIdx,
const bool useFilteredPredSamples,
const bool useISP = false,
const Size cuSize = Size( 0, 0 ) );//角度预测
#else
void xPredIntraAng ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const uint32_t dirMode, const ClpRng& clpRng, const SPS& sps
, int multiRefIdx
, const bool useFilteredPredSamples );
#endif
#endif
Pel xGetPredValDc ( const CPelBuf &pSrc, const Size &dstSize );//得到DC填充值
void xFillReferenceSamples ( const CPelBuf &recoBuf, Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu );//填充参考像素
void xFilterReferenceSamples ( const Pel* refBufUnfiltered, Pel* refBufFiltered, const CompArea &area, const SPS &sps//滤波参考像素
, int multiRefIdx
);
#if HEVC_USE_DC_PREDFILTERING
// dc filtering
void xDCPredFiltering ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType &channelType );
#endif
static int getWideAngle ( int width, int height, int predMode );//静态函数:处理静态数据,获取宽角度
void setReferenceArrayLengths ( const CompArea &area );//设置参考像素的长度
void destroy ();//析构时使用
void xFilterGroup ( Pel* pMulDst[], int i, Pel const* const piSrc, int iRecStride, bool bAboveAvaillable, bool bLeftAvaillable);//没有用上
void xGetLMParameters(const PredictionUnit &pu, const ComponentID compID, const CompArea& chromaArea, int& a, int& b, int& iShift);//获取LM的系数
protected里,VTM5.0中,对帧内预测的参数用一个类来进行管理,还增加了一种宽角度模式与滤波器之间统一的方法。
void initPredIntraParams ( const PredictionUnit & pu, const CompArea compArea, const SPS& sps );//初始化帧内预测参数
#if JVET_N0435_WAIP_HARMONIZATION
static bool isIntegerSlope(const int absAng) { return (0 == (absAng & 0x1F)); }//统一宽角度模式与滤波器
#else
static bool isIntegerSlope ( const int absAng ) { return (0 == (absAng & 0x1F)) && absAng <=32; } // integer-slope modes 2, DIA_IDX and VDIA_IDX. "absAng <=32" restricts wide-angle integer modes
#endif
#if JVET_N0413_RDPCM
void xPredIntraBDPCM ( const CPelBuf &pSrc, PelBuf &pDst, const uint32_t dirMode, const ClpRng& clpRng );//帧内RDPCM预测
#endif
public:
IntraPrediction();//构造
virtual ~IntraPrediction();//析构
void init (ChromaFormat chromaFormatIDC, const unsigned bitDepthY);//初始化
// Angular Intra
void predIntraAng ( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu, const bool useFilteredPredSamples );
#if JVET_M0043_LWIP_MEMOPT
void predIntraLwip ( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu );
#endif
Pel* getPredictorPtr (const ComponentID compID, const bool bUseFilteredPredictions = false) { return m_piYuvExt[compID][bUseFilteredPredictions?PRED_BUF_FILTERED:PRED_BUF_UNFILTERED]; }//获取像素值要填充的位置
// Cross-component Chroma
void predIntraChromaLM(const ComponentID compID, PelBuf &piPred, const PredictionUnit &pu, const CompArea& chromaArea, int intraDir);//LM预测
void xGetLumaRecPixels(const PredictionUnit &pu, CompArea chromaArea);//获取LM下采样后亮度重建值
/// set parameters from CU data for accessing intra data
void initIntraPatternChType (const CodingUnit &cu, const CompArea &area, const bool bFilterRefSamples = false );//初始化帧内预测
static bool useFilteredIntraRefSamples( const ComponentID &compID, const PredictionUnit &pu, bool modeSpecific, const UnitArea &tuArea );//使用滤波的吗?
static bool useDPCMForFirstPassIntraEstimation(const PredictionUnit &pu, const uint32_t &uiDirMode);
void geneWeightedPred (const ComponentID compId, PelBuf &pred, const PredictionUnit &pu, Pel *srcBuf);
Pel* getPredictorPtr2 (const ComponentID compID, uint32_t idx) { return m_yuvExt2[compID][idx]; }
void switchBuffer (const PredictionUnit &pu, ComponentID compID, PelBuf srcBuff, Pel *dst);
void geneIntrainterPred (const CodingUnit &cu);
#if JVET_M0043_LWIP_MEMOPT
void prepareBlockLwip ( const PredictionUnit &pu );
#endif
public里,VTM5.0中主要增加了对ALWIP矩阵帧内预测的具体实现。
#if JVET_N0217_MATRIX_INTRAPRED
// Matrix-based intra prediction
void initIntraMip (const PredictionUnit &pu);//初始化ALWIP
void predIntraMip (const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu);//进行矩阵帧内预测
#endif
#if HM_MDIS_AS_IN_JEM && JVET_N0193_LFNST
static bool getPlanarMDISCondition( const UnitArea &tuArea ) { return abs( PLANAR_IDX - HOR_IDX ) > m_aucIntraFilter[ CHANNEL_TYPE_LUMA ][ ( ( g_aucLog2[ tuArea.Y().width ] + g_aucLog2[ tuArea.Y().height ] ) >> 1 ) ]; }
#endif
// ====================================================================================================================
// Constructor / destructor / initialize
// ====================================================================================================================
IntraPrediction::IntraPrediction()//构造函数,给指针设定初始位置nullptr
:
m_currChromaFormat( NUM_CHROMA_FORMAT )//:构造函数的初始列,赋初值
{
for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)//遍历通道
{
for (uint32_t buf = 0; buf < NUM_PRED_BUF; buf++)//遍历内存
{
m_piYuvExt[ch][buf] = nullptr;//设为nullptr
}
}
for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
{
for (uint32_t buf = 0; buf < 4; buf++)
{
m_yuvExt2[ch][buf] = nullptr;
}
}
m_piTemp = nullptr;//暂存像素值指针
m_pMdlmTemp = nullptr;//MDLM指针
}
IntraPrediction::~IntraPrediction()//析构函数,析构前调用destroy()函数
{
destroy();
}
void IntraPrediction::destroy()//析构操作,这里是构造函数的反向操作
{
for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
{
for (uint32_t buf = 0; buf < NUM_PRED_BUF; buf++)
{
delete[] m_piYuvExt[ch][buf];//delete[]配合new m_piYuvExt[],这里对应*m_piYuvExt
m_piYuvExt[ch][buf] = nullptr;
}
}
for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
{
for (uint32_t buf = 0; buf < 4; buf++)
{
delete[] m_yuvExt2[ch][buf];
m_yuvExt2[ch][buf] = nullptr;
}
}
delete[] m_piTemp;
m_piTemp = nullptr;
delete[] m_pMdlmTemp;
m_pMdlmTemp = nullptr;
//可以看出,析构一个指针类型,需要用delete[]
}
void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepthY)//初始化
{
/************ 如果之前已初始化,但色度格式已更改,释放内存并重新开始 **************/
// if it has been initialised before, but the chroma format has changed, release the memory and start again.
if (m_piYuvExt[COMPONENT_Y][PRED_BUF_UNFILTERED] != nullptr && m_currChromaFormat != chromaFormatIDC)
{
destroy();
}
if (m_yuvExt2[COMPONENT_Y][0] != nullptr && m_currChromaFormat != chromaFormatIDC)
{
destroy();
}
m_currChromaFormat = chromaFormatIDC;
/********************* 进行构造,初始化内存及长度 ************************/
if (m_piYuvExt[COMPONENT_Y][PRED_BUF_UNFILTERED] == nullptr) // check if first is null (in which case, nothing initialised yet)
{
m_iYuvExtSize = (MAX_CU_SIZE * 2 + 1 + MAX_REF_LINE_IDX * 33) * (MAX_CU_SIZE * 2 + 1 + MAX_REF_LINE_IDX * 33);
for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
{
for (uint32_t buf = 0; buf < NUM_PRED_BUF; buf++)
{
m_piYuvExt[ch][buf] = new Pel[m_iYuvExtSize];
}
}
}
if (m_yuvExt2[COMPONENT_Y][0] == nullptr) // check if first is null (in which case, nothing initialised yet)
{
m_yuvExtSize2 = (MAX_CU_SIZE) * (MAX_CU_SIZE);
for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
{
for (uint32_t buf = 0; buf < 4; buf++)
{
m_yuvExt2[ch][buf] = new Pel[m_yuvExtSize2];
}
}
}
if (m_piTemp == nullptr)
{
m_piTemp = new Pel[(MAX_CU_SIZE + 1) * (MAX_CU_SIZE + 1)];
}
if (m_pMdlmTemp == nullptr)
{
m_pMdlmTemp = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];//MDLM will use top-above and left-below samples.
}
}
全局函数是整个程序中共用的,这些函数在整个程序的任何地方都允许被调用,也就是说不单单属于IntraPrediction类。
这里有五个,都是获取参考像素时用的,为isAboveLeftAvailable、isLeftAvailable、isLeftAvailable、isAboveRightAvailable、isBelowLeftAvailable。
/****************** 左上角是否可用 ***************/
bool isAboveLeftAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posLT)
{
const CodingStructure& cs = *cu.cs;
const Position refPos = posLT.offset(-1, -1);//得到左上角像素的左上位置
const CodingUnit* pcCUAboveLeft = cs.isDecomp( refPos, chType ) ? cs.getCURestricted( refPos, cu, chType ) : nullptr;//这个像素点是否存在且可用,可以则指向其CU,否则设为空指针
const bool isConstrained = cs.pps->getConstrainedIntraPred();//是否限制了帧内预测
bool bAboveLeftFlag;
if (isConstrained)//若限制了:左上角可用且是帧内预测出来的,才可以
{
bAboveLeftFlag = pcCUAboveLeft && CU::isIntra(*pcCUAboveLeft);
}
else//无限制:左上角可用则可用
{
bAboveLeftFlag = (pcCUAboveLeft ? true : false);
}
return bAboveLeftFlag;//返回是否可用
}
/****************** 上面有几个可用的点 ***************/
int isAboveAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *bValidFlags)
{
const CodingStructure& cs = *cu.cs;
const bool isConstrained = cs.pps->getConstrainedIntraPred();//是否限制了帧内预测
bool *pbValidFlags = bValidFlags;//该像素点是否可用
int iNumIntra = 0;//数量
int maxDx = uiNumUnitsInPU * unitWidth;
for (uint32_t dx = 0; dx < maxDx; dx += unitWidth)
{
const Position refPos = posLT.offset(dx, -1);//得到上一排像素
const CodingUnit* pcCUAbove = cs.isDecomp(refPos, chType) ? cs.getCURestricted(refPos, cu, chType) : nullptr;//这个像素点是否存在且可用,可以则指向其CU,否则设为空指针
if( pcCUAbove && ( ( isConstrained && CU::isIntra( *pcCUAbove ) ) || !isConstrained ) )//如果存在且满足限制条件,个数+1,可用的标志设为true
{
iNumIntra++;//个数+1
*pbValidFlags = true;//该像素点存在且可用
}
else if( !pcCUAbove )//如果不存在,计数停止,直接返回
{
return iNumIntra;
}
pbValidFlags++;//指向下一个位置(右边一个)
}
return iNumIntra;//返回可用参考像素的个数
}
/***以下同上***/
/****************** 左面有几个可用的点 ***************/
int isLeftAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *bValidFlags)
{
const CodingStructure& cs = *cu.cs;
const bool isConstrained = cs.pps->getConstrainedIntraPred();
bool *pbValidFlags = bValidFlags;
int iNumIntra = 0;
int maxDy = uiNumUnitsInPU * unitHeight;
for (uint32_t dy = 0; dy < maxDy; dy += unitHeight)
{
const Position refPos = posLT.offset(-1, dy);
const CodingUnit* pcCULeft = cs.isDecomp(refPos, chType) ? cs.getCURestricted(refPos, cu, chType) : nullptr;
if( pcCULeft && ( ( isConstrained && CU::isIntra( *pcCULeft ) ) || !isConstrained ) )
{
iNumIntra++;
*pbValidFlags = true;
}
else if( !pcCULeft )
{
return iNumIntra;
}
pbValidFlags--; // opposite direction
}
return iNumIntra;
}
/****************** 右上有几个可用的点 ***************/
int isAboveRightAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posRT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *bValidFlags )
{
const CodingStructure& cs = *cu.cs;
const bool isConstrained = cs.pps->getConstrainedIntraPred();
bool *pbValidFlags = bValidFlags;
int iNumIntra = 0;
uint32_t maxDx = uiNumUnitsInPU * unitWidth;
for (uint32_t dx = 0; dx < maxDx; dx += unitWidth)
{
const Position refPos = posRT.offset(unitWidth + dx, -1);
const CodingUnit* pcCUAbove = cs.isDecomp(refPos, chType) ? cs.getCURestricted(refPos, cu, chType) : nullptr;
if( pcCUAbove && ( ( isConstrained && CU::isIntra( *pcCUAbove ) ) || !isConstrained ) )
{
iNumIntra++;
*pbValidFlags = true;
}
else if( !pcCUAbove )
{
return iNumIntra;
}
pbValidFlags++;
}
return iNumIntra;
}
/****************** 左下有几个可用的点 ***************/
int isBelowLeftAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posLB, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *bValidFlags )
{
const CodingStructure& cs = *cu.cs;
const bool isConstrained = cs.pps->getConstrainedIntraPred();
bool *pbValidFlags = bValidFlags;
int iNumIntra = 0;
int maxDy = uiNumUnitsInPU * unitHeight;
for (uint32_t dy = 0; dy < maxDy; dy += unitHeight)
{
const Position refPos = posLB.offset(-1, unitHeight + dy);
const CodingUnit* pcCULeft = cs.isDecomp(refPos, chType) ? cs.getCURestricted(refPos, cu, chType) : nullptr;
if( pcCULeft && ( ( isConstrained && CU::isIntra( *pcCULeft ) ) || !isConstrained ) )
{
iNumIntra++;
*pbValidFlags = true;
}
else if ( !pcCULeft )
{
return iNumIntra;
}
pbValidFlags--; // opposite direction
}
return iNumIntra;
}
通过对这个类的总结,对前面C++学习1-10可以说有了进一步的巩固,当然更重要的是后面的工作将在VTM5.0上开展,新技术的了解和代码的学习又是一道道难关,革命尚未成功,同志仍需努力。。。