Planar模式和DC模式时两种特殊的角度模式,分别对应于模式号0和1。
DC模式适用于大面积平坦区域,其预测值是通过计算左边和(或)上边参考像素的平均值获得的。
DC模式预测值的计算和块的形状有关:
xPredIntraDc函数是DC模式的入口函数,主要功能是调用xGetPredValDc函数生成DC值,然后将DC值填充整个块
void IntraPrediction::xPredIntraDc( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const bool enableBoundaryFilter )
{
//DC模式适用于大面积平坦区域,其预测值由其左侧和(或)上侧参考像素值得到。
const Pel dcval = xGetPredValDc( pSrc, pDst );//生成DC值
pDst.fill( dcval );//填充
}
xGetPredValDc函数用来生成DC值
// Function for calculating DC value of the reference samples used in Intra prediction
//NOTE: Bit-Limit - 25-bit source
Pel IntraPrediction::xGetPredValDc( const CPelBuf &pSrc, const Size &dstSize )
{
CHECK( dstSize.width == 0 || dstSize.height == 0, "Empty area provided" );
int idx, sum = 0;
Pel dcVal;
const int width = dstSize.width;
const int height = dstSize.height;
const auto denom = (width == height) ? (width << 1) : std::max(width,height);
const auto divShift = floorLog2(denom);
const auto divOffset = (denom >> 1);
if ( width >= height )//宽大于高时,只用预测像素宽的平均值
{
for( idx = 0; idx < width; idx++ )
{
sum += pSrc.at(m_ipaParam.multiRefIndex + 1 + idx, 0);
}
}
if ( width <= height )//宽小于高时,只用预测像素高的平均值
{
for( idx = 0; idx < height; idx++ )
{
sum += pSrc.at(m_ipaParam.multiRefIndex + 1 + idx, 1);
}
}
dcVal = (sum + divOffset) >> divShift;//四舍五入求平均值
return dcVal;
}
Planar模式适用于像素渐变的情况,即适用于像素值缓慢变换的区域。
计算方法:根据a,b,c,d求加权平均,权重与距离有关;
具体计算公式如下:
横向预测值为 pred_h(x,y) = (width-x-1)*b + (x+1)*a
纵向预测值为 pred_v(x,y) = (height-y-1)*d + (y+1)*c
最终预测值为 pred(x,y) = (pred_h(x,y) + pred_v(x,y) + offset) >> shift
其中,x=0,1···width-1;y=0,1···height-1
/** Function for deriving planar intra prediction. This function derives the prediction samples for planar mode (intra coding).
*/
//NOTE: Bit-Limit - 24-bit source
void IntraPrediction::xPredIntraPlanar( const CPelBuf &pSrc, PelBuf &pDst )
{
const uint32_t width = pDst.width;
const uint32_t height = pDst.height;
#if JVET_P0329_PLANAR_SIMPLIFICATION
const uint32_t log2W = floorLog2( width );
const uint32_t log2H = floorLog2( height );
#else
const uint32_t log2W = floorLog2(width < 2 ? 2 : width);
const uint32_t log2H = floorLog2(height < 2 ? 2 : height);
#endif
int leftColumn[MAX_CU_SIZE + 1], topRow[MAX_CU_SIZE + 1], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];
const uint32_t offset = 1 << (log2W + log2H);//后面用于四舍五入
// Get left and above reference column and row
// 获得参考像素
for( int k = 0; k < width + 1; k++ )
{
topRow[k] = pSrc.at( k + 1, 0 );//获取上一行的像素值
}
for( int k = 0; k < height + 1; k++ )
{
leftColumn[k] = pSrc.at(k + 1, 1);//获取左一列的像素值
}
/**
* Planar模式横向的预测值为 pred_h(x,y) = (width-x-1)*leftcolumn(y) + (x+1)*topRight
* Planar模式纵向的预测值为 pred_v(x,y) = (height-y-1)*topRow(x) + (y+1)*bottomLeft
* 最终预测值为:pred(x,y) = (pred_h + pred_v + offset) >> shift
*
* 代码里将上式调整为:pred_h(x,y) = width*leftcolumn(y) + (x+1)*[topRight-leftcolumn(y)]
* pred_v(x,y) = height*topRow(x) + (y+1)*[bottomLeft-topRow(x)]
**/
// Prepare intermediate variables used in interpolation
// 准备用于插值的中间变量
int bottomLeft = leftColumn[height];//左边界下一个
int topRight = topRow[width];//上边界右一位
//填充四个边缘的值
for( int k = 0; k < width; k++ )
{
bottomRow[k] = bottomLeft - topRow[k];
topRow[k] = topRow[k] << log2H;//实现height*topRow(x)
}
for( int k = 0; k < height; k++ )
{
rightColumn[k] = topRight - leftColumn[k];
leftColumn[k] = leftColumn[k] << log2W;//实现width*leftcolumn(y)
}
//填充Planar模式CU内部的像素值
const uint32_t finalShift = 1 + log2W + log2H;
const uint32_t stride = pDst.stride;
Pel* pred = pDst.buf;//预测像素的首地址
for( int y = 0; y < height; y++, pred += stride )
{
int horPred = leftColumn[y];//水平方向预测值
for( int x = 0; x < width; x++ )
{
horPred += rightColumn[y];//累加,通过循环实现(x+1)*[topRight-leftcolumn(y)]
topRow[x] += bottomRow[x];//累加,通过循环实现(y+1)*[bottomLeft-topRow(x)]
int vertPred = topRow[x];//垂直方向预测值
//水平方向预测像素的权重和高度有关,垂直方向的预测像素的权重和宽度有关
pred[x] = ( ( horPred << log2H ) + ( vertPred << log2W ) + offset ) >> finalShift;//加权后求平均值,获得最终预测像素
}
}
}