void IntraPrediction::xPredIntraPlanar( const CPelBuf &pSrc, PelBuf &pDst )
{
//块的宽高
const uint32_t width = pDst.width;
const uint32_t height = pDst.height;
const uint32_t log2W = floorLog2( width );
const uint32_t log2H = floorLog2( height );
// 存储数据
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
CHECK(width > MAX_CU_SIZE, "width greater than limit");
//上方参考像素
for( int k = 0; k < width + 1; k++ )
{
topRow[k] = pSrc.at( k + 1, 0 );
}
CHECK(height > MAX_CU_SIZE, "height greater than limit");
//左列参考像素
for( int k = 0; k < height + 1; k++ )
{
leftColumn[k] = pSrc.at(k + 1, 1);
}
// Prepare intermediate variables used in interpolation
//准备插值中使用的中间变量
int bottomLeft = leftColumn[height];
int topRight = topRow[width];
这是正式标准的算法
以8x8的块为例
参考像素为蓝色(x = −1, y = −1..nTbH)和粉色(x = 0..nTbW, y = −1)
注意: pSrc.at( k + 1, 0 )这里K+1就是为了不把左上角参考像素存进数组,以64X64为例,一共存入65个值。topRow[64]即第65个值
bottomLeft:就是a
topRight:是c
a和c的值不会变
然后再根据当前预测像素的位置,结合b,d得出像素预测值
//
for( int k = 0; k < width; k++ )
{
bottomRow[k] = bottomLeft - topRow[k];
topRow[k] = topRow[k] << log2H;
}
for( int k = 0; k < height; k++ )
{
rightColumn[k] = topRight - leftColumn[k];
leftColumn[k] = leftColumn[k] << log2W;
}
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];
topRow[x] += bottomRow[x];
int vertPred = topRow[x];
pred[x] = ( ( horPred << log2H ) + ( vertPred << log2W ) + offset ) >> finalShift;
}
}
}
具体计算公式:
bottomRow[],rightColumn[]:a,c通过与leftColumn,topRow相互计算,得出的值填充并形成块右,块下参考像素
finalShift:就是Log2 ( nTbW ) + Log2 ( nTbH ) + 1
offset:就是nTbW * nTbH
leftColumn[k]' = leftColumn[k] *width:这一步没看懂什么意思,好像是什么线性滤波器的效果,之后再看
pred += stride:因为预测值是一行一行,一个个元素存进去的。所以每存完一行像素的预测值就需要加上这一行所有像素的地址。然后再开始新的一行新的首地址来存储预测值,pred[x],x在变,存的一行中一个个像素的预测值,pred首地址在变,每一个首地址代表一行像素的预测值。用stride是因为可能到了后几个CTU,会进行填充才能划分出一个CTU,所以用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];//垂直方向预测值
horPred:用来存水平预测值。在x循环时的horPred才是最终值,在y循环时只是先建立这个变量
代码实现效果:horPred = 这一行前面的像素水平预测值之和与右侧参考像素相加所得值
vertPred = 这一列前面的像素垂直预测值之和与下边参考像素值相加所得值
horPred最终可简化为:
leftColumn[k]' = leftColumn[k] *width
rightColumn[k] = topRight - leftColumn[k]x=0 horPred =horPred+ rightColumn[y]
=leftColumn[y] *width+topRight - leftColumn[y]x=1 horPred =leftColumn[y] *width+topRight - leftColumn[y]+topRight - leftColumn[y]
x=2 horPred =leftColumn[y] *width+topRight - leftColumn[y]+topRight - leftColumn[y]+topRight - leftColumn[y]
horPred = leftColumn[y] *width+(x+1)(topRight - leftColumn[y])
vertPred,因为每一个y的循环里,topRow[x]都在累加,然后赋给vertPred,所以可简化为:
topRow[k] ' = topRow[k] *height
bottomRow[k] = bottomLeft - topRow[k]y=0 vertPred = topRow[x] + bottomRow[x]
= topRow[x] *height + bottomLeft - topRow[x]y=1 vertPred = topRow[x] *height + bottomLeft - topRow[x]+ bottomLeft - topRow[x]
y=3 vertPred = topRow[x] *height + bottomLeft - topRow[x]+ bottomLeft - topRow[x]+ bottomLeft - topRow[x]
vertPred = topRow[x] *height +(y+1)(bottomLeft - topRow[x])
与算法描述完全一致
2.DC模式
void IntraPrediction::xPredIntraDc( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const bool enableBoundaryFilter )
{
const Pel dcval = xGetPredValDc( pSrc, pDst );
pDst.fill( dcval );
}
从函数中得到dcval值,然后填充到pDst里
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; //DC值
const int width = dstSize.width;
const int height = dstSize.height;
//若width == height,则为2*width
//若width != height,则为max(width, height)
const auto denom = (width == height) ? (width << 1) : std::max(width,height);
//宽高不相等时,divShift 要小一倍
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;
}
divShift:=( Log2( nTbW ) + 1 )
multiRefIndex :这个与MRL有关,但一般默认关闭
宽大于高的情况,即上参考像素之和。高大于宽也是相同道理:
width >= height
idx = 0 sum = pSrc.at( 1 + idx, 0)
idx = 1 sum = pSrc.at( 1 , 0) + pSrc.at( 1 + idx, 0)
idx = 2 sum = pSrc.at( 1 , 0) + pSrc.at( 2, 0) + pSrc.at( 1 + idx, 0)
而块相等的时候,因为if条件都满足,所以是两个sum的和 。
这里以64X64的块为例,与算法一致
denom =128;divShift = 7 ;divOffset = 64
dcVal = (sum + 64) >> 7;
最后返回 dcVal值
注意:the neighbouring samples p[ x ][ y ], with x = −1 − refIdx, y = 0..nTbH − 1 and x = 0..nTbW − 1, y = −1 − refIdx
这里显示没有像planar模式一样,用到左下角,右上方的参考像素