矩陣加權幀內預測(Matrix weighted intra prediction,MIP)是VTM5.0中新加的幀內預測技術。該技術的核心就是訓練矩陣,當前的亮度預測塊的相鄰採樣點首先進行平均操作減少採樣點數目,構成一個向量,然後該向量和視頻序列集訓練的一個參數矩陣相乘,通過該矩陣和向量相乘得到一個部分預測值的散點陣列,最後通過雙線性插值生成出最終的預測值。
如果預測一個WxH的PU,MIP使用上邊的W個重建像素,左邊的H個重建像素作爲參考像素。參考像素的生成方法和普通的幀內預測一樣。然後經過平均、矩陣向量相乘、線性插值三個步驟得到最終預測值。
1、平均
首先需要從左邊參考列和上邊參考行通過鄰域平均的方法取參考樣點。當W=H=4時,左邊取2個上邊取2個,共4個。其他情況下左邊取4個,上邊取4個,共8個。如上圖左邊部分所示。
// init reduced boundary size
if (m_blockSize.width > 4 || m_blockSize.height > 4)
{
m_reducedBoundarySize = Size(4, 4);
}
else
{
m_reducedBoundarySize = Size(2, 2);
}
計算鄰域平均是個下采樣過程,如下:
void PredictorMIP::doDownsampling( int* dst, const int* src, const SizeType srcLen, const SizeType dstLen )
{
// TODO: Check if src and dst can ever be negative. If not assign unsigned type and simplify rounding.
const SizeType downsmpFactor = srcLen / dstLen;
CHECKD( srcLen != dstLen * downsmpFactor, "Need integer downsampling factor." );
CHECKD( ( downsmpFactor & ( downsmpFactor - 1 ) ) != 0, "Need power of two downsampling factor." );
const int log2DownsmpFactor = g_aucLog2[ downsmpFactor ];
const int roundingOffsetPositive = ( 1 << ( log2DownsmpFactor - 1 ) );
for( SizeType srcIdx = 0, dstIdx = 0; dstIdx < dstLen; ++dstIdx )
{
int sum = 0;
for( SizeType blockIdx = 0; blockIdx < downsmpFactor; ++blockIdx, ++srcIdx )
{
sum += src[ srcIdx ];
}
const int roundingOffset = roundingOffsetPositive - ( sum < 0 ? 1 : 0 );
dst[ dstIdx ] = ( sum + roundingOffset ) >> log2DownsmpFactor;
}
}
取出的參考樣點要拼接成長度爲4或8的向量。拼接方式如下:
其中mode指的是MIP-mode。
#if JVET_N0217_MATRIX_INTRAPRED
int getNumModesMip(const Size& block)
{
if (block.width > (4 * block.height) || block.height > (4 * block.width))
{
return 0;
}
if( block.width == 4 && block.height == 4 )
{
return 35;
}
else if (block.width <= 8 && block.height <= 8)
{
return 19;
}
else
{
return 11;
}
}
bool PredictorMIP::isTransposed( const int modeIdx ) const
{
return ( modeIdx > ( m_numModes / 2 ) );
}
2、矩陣向量相乘
上一步得到的向量和一個訓練好的矩陣相乘,再加上一個偏移量得到預測值。
其中矩陣A和向量b都是訓練好的,取自於集合S0,S1或S2。
int PredictorMIP::getWeightIdx( const int modeIdx ) const
{//!<對於模式號大於m_numModes / 2 的需要進行摺疊和小於m_numModes / 2 的使用相同矩陣
if( modeIdx > m_numModes / 2 )
{
return modeIdx - m_numModes / 2;
}
else
{
return modeIdx;
}
}
void PredictorMIP::getMatrixBias( const short*& matrix, const short*& bias, const int modeIdx ) const
{
const int idx = getWeightIdx( modeIdx );
//!<4x4塊使用mipMatrix4x4和mipBias4x4裏的矩陣和向量,下同
if( m_blockSize.width == 4 && m_blockSize.height == 4 )
{
matrix = &mipMatrix4x4[idx][0][0];
bias = &mipBias4x4 [idx][0];
}
else if( m_blockSize.width <= 8 && m_blockSize.height <= 8 )
{
matrix = &mipMatrix8x8[idx][0][0];
bias = &mipBias8x8 [idx][0];
}
else
{
matrix = &mipMatrix16x16[idx][0][0];
bias = &mipBias16x16 [idx][0];
}
}
上面代碼中mipMatrix4x4和mipBias4x4就是S0,mipMatrix8x8和mipBias8x8就是S1,mipMatrix16x16和mipBias16x16就是S2。
mipMatrix4x4裏含18個16x4的矩陣,mipBias4x4裏含18個16維的向量,用於4x4的塊。
mipMatrix8x8裏含10個16x8的矩陣,mipBias8x8裏含10個16維的向量,用於4x8,8x4,8x8塊。
mipMatrix16x16裏含6個64x8的矩陣,mipBias16x16裏含6個64維的向量,用於其他塊。
由於篇幅限制,具體的矩陣和向量值就不貼了。
注:
1、由getNumModesMip函數可知,4x4塊有35種MIP模式,4x8,8x4,8x8塊有19種MIP模式,其他情況有11種模式。而這裏S0,S1,S2長度分別是18,10,6,比之前減半了。注意由getWeightIdx可知每2種模式共享一個矩陣和向量。
2、對於4xn,nx4(n>8)的塊,由上面公式可知其對應的矩陣A應該是32x8,這裏使用S2內的矩陣和向量進行計算,其中會丟掉矩陣中的32行和向量中的32個值。丟掉的行分別對應於生成的8x8矩陣中的x奇數位置和y的奇數位置。
3、插值
插值是一個上採樣過程。
如果兩個方向都需要插值,如果W<H先進行水平方向插值否則先進行垂直方向插值。
以WxH塊的垂直插值爲例,其中max(W,H)≧8且W≧H。
首先,將待插值的矩陣擴展到上面一行,如圖1右半部分所示。上邊一行按下式得到。
然後,對空餘位置利用相鄰值按下式進行插值。
從上面公式可以看出,當上採樣2倍時即U_ver=2或H_ver=2,進行插值計算時不需要乘法運算。在其他情況下每次插值計算會進行不多於2次乘法運算。
void PredictorMIP::predictionUpsampling1D( int* const dst, const int* const src, const int* const bndry,
const SizeType srcSizeUpsmpDim, const SizeType srcSizeOrthDim,
const SizeType srcStep, const SizeType srcStride,
const SizeType dstStep, const SizeType dstStride,
const unsigned int upsmpFactor )
{
// TODO: Check if src and dst can ever be negative. If not assign unsigned type and simplify rounding.
const int log2UpsmpFactor = g_aucLog2[ upsmpFactor ];
CHECKD( upsmpFactor <= 1, "Upsampling factor must be at least 2." );
SizeType idxOrthDim = 0;
const int* srcLine = src;
int* dstLine = dst;
while( idxOrthDim < srcSizeOrthDim )
{
SizeType idxUpsmpDim = 0;
const int* before = bndry + idxOrthDim;
const int* behind = srcLine;
int* currDst = dstLine;
while( idxUpsmpDim < srcSizeUpsmpDim )
{
SizeType pos = 1;
int scaledBefore = ( *before ) << log2UpsmpFactor;
int scaledBehind = 0;
while( pos <= upsmpFactor )
{
scaledBefore -= *before;
scaledBehind += *behind;
*currDst = scaledBefore + scaledBehind;
*currDst = ( *currDst + ( 1 << ( log2UpsmpFactor - 1 ) ) -
( *currDst < 0 ? 1 : 0 ) ) >> log2UpsmpFactor;
pos++;
currDst += dstStep;
}
idxUpsmpDim++;
before = behind;
behind += srcStep;
}
idxOrthDim++;
srcLine += srcStride;
dstLine += dstStride;
}
}
4、示例
下面給出一個完整的MIP算法過程是示例。
1.對於4x4的塊,從左邊和上邊參考行各取兩個值組成一個4維的向量。將該向量進行矩陣向量相乘運算,矩陣取自於S0。完成矩陣向量相乘和偏移值相加後得到一個16維的向量。這個16維向量形成一個4x4的矩陣不用進行插值直接作爲預測矩陣。預測矩陣的每個值需要進行(4x16)/(4x4)=4次乘法運算。
2.對於8x8的塊,從左邊和上邊參考行各取4個值組成一個8維的向量。將該向量進行矩陣向量相乘運算,矩陣取自於S1。完成矩陣向量相乘和偏移值相加後得到一個16維的向量。這個16維向量形成預測矩陣的奇數位置值。然後在垂直和水平方向進行插值,由於這個插值過程不需要進行乘法運算,所以預測矩陣的每個值需要進行(8x16)/(8x8)=2次乘法運算。
3.對於8x4的塊,從左邊和上邊參考行各取4個值組成一個8維的向量。將該向量進行矩陣向量相乘運算,矩陣取自於S1。完成矩陣向量相乘和偏移值相加後得到一個16維的向量。這個16維向量形成預測矩陣的奇數行值和所有的列值。然後在水平方向進行插值,由於這個插值過程不需要進行乘法運算,所以預測矩陣的每個值需要進行(8x16)/(8x4)=4次乘法運算。4x8塊處理過程類似。
4.對於16x16的塊,從左邊和上邊參考行各取4個值組成一個8維的向量。將該向量進行矩陣向量相乘運算,矩陣取自於S2。完成矩陣向量相乘和偏移值相加後得到一個64維的向量。這個64維向量形成預測矩陣的奇數行值和奇數列值。然後在垂直和水平方向進行插值,由於插值過程不需要進行乘法運算,所以預測矩陣的每個值需要進行(8x64)/(16x16)=2次乘法運算。
對於更大尺寸的塊處理過程類似,很容易計算出每個樣點乘法運算次數小於4。
對於Wx8,W>8的塊,生成的8x8預測矩陣包含所有行和奇數列,所以只需要在水平方向進行插值。預測矩陣的每個值需要進行(8x64)/(Wx8)=64/W次乘法運算。當W=16時,它的線性插值過程不需要乘法運算。當W>16時,每個像素插值過程需要的乘法運算少於2次。所以每個像素總的乘法運算小於等於4次。8xW類似。
對於Wx4的塊,矩陣相乘時要去掉矩陣的奇數行,最終只需要在水平方向進行插值。預測矩陣的每個值需要進行(8x32)/(Wx4)=64/W次乘法運算。當W=16時,它的線性插值過程不需要乘法運算。當W>16時,每個像素插值過程需要的乘法運算少於2次。所以每個像素總的乘法運算小於等於4次。4xW類似。
感興趣的請關注微信公衆號Video Coding