候选模式列表的建立
注意:如果没有特别说明,操作的对象都是亮度块(Luma)
帧内预测也可以分成两种方式,一种是常规方式,另一种是快速方式
在常规方式
所有的35种帧内预测模式都被添加到候选模式列表中,在后续的惭怍中,对每一个模式都进行预测、变换、量化等操作,速度很慢。
相关代码在estIntraPredQT函数中
UInt uiRdModeList[FAST_UDI_MAX_RDMODE_NUM];
Int numModesForFullRD = g_aucIntraModeNumFast[ uiWidthBit ];//8
Bool doFastSearch = (numModesForFullRD != numModesAvailable);//true
// 使用快速模式
if (doFastSearch)
{
// 省略快速模式的实现代码
}
// 使用常规方式
else
{
for( Int i=0; i < numModesForFullRD; i++)
{
uiRdModeList[i] = i;
}
}
在快速方式
先遍历35种帧内预测模式,对于每一种模式都进行预测操作,选出代价最优的几种模式作为候选模式,添加到候选模式列表中。
同时,因为相邻的PU通常具有很强的相关性,因此,可以使用相邻PU的帧内模式来预测当前PU的帧内模式。预测之后可以得到若干个模式,把这些模式加入模式候选列表中。
在后续的操作中,只对这几种模式进行预测、变换、量化等操作,因为候选列表中的模式的数量比较少,因此速度会很快。
相关代码在estIntraPredQT函数中
UInt uiRdModeList[FAST_UDI_MAX_RDMODE_NUM];
Int numModesForFullRD = g_aucIntraModeNumFast[ uiWidthBit ];//8
Bool doFastSearch = (numModesForFullRD != numModesAvailable);//true
// 使用快速模式
if (doFastSearch)
{
assert(numModesForFullRD < numModesAvailable);
for( Int i=0; i < numModesForFullRD; i++ )
{
// 用于存储每一种模式的消耗
CandCostList[ i ] = MAX_DOUBLE;
}
CandNum = 0;
// 遍历35种帧内预测模式,选取若干个代价比较小的模式作为后续处理的模式
for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ ) // 总共有35种模式,numModesAvailable = 35
{
UInt uiMode = modeIdx;
// 对亮度块进行预测
predIntraLumaAng( pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, bAboveAvail, bLeftAvail );
// use hadamard transform here
// 使用hadamard变换,计算SATD的值
UInt uiSad = m_pcRdCost->calcHAD(g_bitDepthY, piOrg, uiStride, piPred, uiStride, uiWidth, uiHeight );
UInt iModeBits = xModeBitsIntra( pcCU, uiMode, uiPU, uiPartOffset, uiDepth, uiInitTrDepth );
// 计算此种模式的代价
Double cost = (Double)uiSad + (Double)iModeBits * m_pcRdCost->getSqrtLambda();
// 更新候选列表
CandNum += xUpdateCandList( uiMode, cost, numModesForFullRD, uiRdModeList, CandCostList );
}
#if FAST_UDI_USE_MPM
Int uiPreds[3] = {-1, -1, -1};
Int iMode = -1;
// 根据相邻块的预测模式来对当前块的模式进行预测,得到若干模式(称为预测模式),存放在uiPreds中
Int numCand = pcCU->getIntraDirLumaPredictor( uiPartOffset, uiPreds, &iMode );
if( iMode >= 0 )
{
// 将候选列表的索引设置为此模式
numCand = iMode;
}
// 遍历预测的模式,如果它不在模式候选列表中,那么把它添加到模式候选列表中
for( Int j=0; j < numCand; j++)
{
Bool mostProbableModeIncluded = false;
Int mostProbableMode = uiPreds[j];
for( Int i=0; i < numModesForFullRD; i++)
{
mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);
}
if (!mostProbableModeIncluded)
{
uiRdModeList[numModesForFullRD++] = mostProbableMode;
}
}
#endif // FAST_UDI_USE_MPM
}
// 使用常规方式
else
{
for( Int i=0; i < numModesForFullRD; i++)
{
uiRdModeList[i] = i;
}
}
从所有的35种模式中选出若干最优模式
// 遍历35种帧内预测模式,选取若干个代价比较小的模式作为后续处理的模式
for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ ) // 总共有35种模式,numModesAvailable = 35
{
UInt uiMode = modeIdx;
// 对亮度块进行预测
predIntraLumaAng( pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, bAboveAvail, bLeftAvail );
// use hadamard transform here
// 使用hadamard变换,计算SATD的值
UInt uiSad = m_pcRdCost->calcHAD(g_bitDepthY, piOrg, uiStride, piPred, uiStride, uiWidth, uiHeight );
UInt iModeBits = xModeBitsIntra( pcCU, uiMode, uiPU, uiPartOffset, uiDepth, uiInitTrDepth );
// 计算此种模式的代价
Double cost = (Double)uiSad + (Double)iModeBits * m_pcRdCost->getSqrtLambda();
// 更新候选列表
CandNum += xUpdateCandList( uiMode, cost, numModesForFullRD, uiRdModeList, CandCostList );
}
亮度块的帧内预测函数
// 亮度块的帧内预测
Void TComPrediction::predIntraLumaAng(TComPattern* pcTComPattern, UInt uiDirMode, Pel* piPred, UInt uiStride, Int iWidth, Int iHeight, Bool bAbove, Bool bLeft )
{
Pel *pDst = piPred;
Int *ptrSrc;
assert( g_aucConvertToBit[ iWidth ] >= 0 ); // 4x 4
assert( g_aucConvertToBit[ iWidth ] <= 5 ); // 128x128
assert( iWidth == iHeight );
// 获取数据的指针
ptrSrc = pcTComPattern->getPredictorPtr( uiDirMode, g_aucConvertToBit[ iWidth ] + 2, m_piYuvExt );
// get starting pixel in block
// 获取块中的开始像素
Int sw = 2 * iWidth + 1; // 9
// Create the prediction
// 如果指定了planar模式
if ( uiDirMode == PLANAR_IDX )
{
// planar预测模式
xPredIntraPlanar( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight );
}
else
{
// 没有指定planar模式
// 区分水平或者垂直模式进行预测
if ( (iWidth > 16) || (iHeight > 16) )
{
xPredIntraAng(g_bitDepthY, ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, uiDirMode, bAbove, bLeft, false );
}
else
{
xPredIntraAng(g_bitDepthY, ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, uiDirMode, bAbove, bLeft, true );
// 观察是否为DC模式
if( (uiDirMode == DC_IDX ) && bAbove && bLeft )
{
// 对DC模式进行滤波
xDCPredFiltering( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight);
}
}
}
}
模式的预测
因为相邻的PU通常具有很强的相关性,因此,可以使用相邻PU的帧内模式来预测当前PU的帧内模式。预测之后可以得到若干个模式,把这些模式加入模式候选列表中。
相关代码在estIntraPredQT函数中
Int uiPreds[3] = {-1, -1, -1};
Int iMode = -1;
// 根据相邻块的预测模式来对当前块的模式进行预测,得到若干模式(称为预测模式),存放在uiPreds中
Int numCand = pcCU->getIntraDirLumaPredictor( uiPartOffset, uiPreds, &iMode );
if( iMode >= 0 )
{
// 将候选列表的索引设置为此模式
numCand = iMode;
}
// 遍历预测的模式,如果它不在模式候选列表中,那么把它添加到模式候选列表中
for( Int j=0; j < numCand; j++)
{
Bool mostProbableModeIncluded = false;
Int mostProbableMode = uiPreds[j];
for( Int i=0; i < numModesForFullRD; i++)
{
mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);
}
if (!mostProbableModeIncluded)
{
uiRdModeList[numModesForFullRD++] = mostProbableMode;
}
}
模式预测函数
预测的步骤如下:
1、假设当前块的左边是A,上面的是B,candList数组用于存放预测的模式
2、如果modeA==modeB
(1)modeA和modeB都是planar或者DC模式,那么candList[0] =planar,candList[1]=DC,candList[2]=26
(2)modeA和modeB都是角度模式,那么,candList[0]=modeA,candList[1]和candList[2]等于modeA相邻的两个模式
3、如果modeA!=modeB,那么candList[0]=modeA,candList[1]=modeB,candList[2]则要分几种情况
(1)modeA和modeB都不是planar模式,那么candList[2]=planar
(2)当(1)不满足,而且modeA和modeB都不是DC模式,那么candList[2]=DC
(3)当(1)和(2)不满足,candList[2]=26
Int TComDataCU::getIntraDirLumaPredictor( UInt uiAbsPartIdx, Int* uiIntraDirPred, Int* piMode )
{
TComDataCU* pcTempCU;
UInt uiTempPartIdx;
Int iLeftIntraDir, iAboveIntraDir;
Int uiPredNum = 0;
// Get intra direction of left PU
pcTempCU = getPULeft( uiTempPartIdx, m_uiAbsIdxInLCU + uiAbsPartIdx );
iLeftIntraDir = pcTempCU ? ( pcTempCU->isIntra( uiTempPartIdx ) ? pcTempCU->getLumaIntraDir( uiTempPartIdx ) : DC_IDX ) : DC_IDX;
// Get intra direction of above PU
pcTempCU = getPUAbove( uiTempPartIdx, m_uiAbsIdxInLCU + uiAbsPartIdx, true, true );
iAboveIntraDir = pcTempCU ? ( pcTempCU->isIntra( uiTempPartIdx ) ? pcTempCU->getLumaIntraDir( uiTempPartIdx ) : DC_IDX ) : DC_IDX;
uiPredNum = 3;
if(iLeftIntraDir == iAboveIntraDir)
{
if( piMode )
{
*piMode = 1;
}
if (iLeftIntraDir > 1) // angular modes
{
uiIntraDirPred[0] = iLeftIntraDir;
uiIntraDirPred[1] = ((iLeftIntraDir + 29) % 32) + 2;
uiIntraDirPred[2] = ((iLeftIntraDir - 1 ) % 32) + 2;
}
else //non-angular
{
uiIntraDirPred[0] = PLANAR_IDX;
uiIntraDirPred[1] = DC_IDX;
uiIntraDirPred[2] = VER_IDX;
}
}
else
{
if( piMode )
{
*piMode = 2;
}
uiIntraDirPred[0] = iLeftIntraDir;
uiIntraDirPred[1] = iAboveIntraDir;
if (iLeftIntraDir && iAboveIntraDir ) //both modes are non-planar
{
uiIntraDirPred[2] = PLANAR_IDX;
}
else
{
uiIntraDirPred[2] = (iLeftIntraDir+iAboveIntraDir)<2? VER_IDX : DC_IDX;
}
}
return uiPredNum;
}