H.266/VVC专栏传送
上一篇:H.266/VVC-VTM代码学习-帧内预测06-VVC的宽角度预测模式getModifiedWideAngle函数分析
下一篇:H.266/VVC-VTM代码学习-帧内预测08-MIP模式(2)predBlock函数完成Mip预测
目录
前言
VTM是H.266/VVC视频编码标准的参考软件,研究VTM代码给研究人员解释了VVC编码标准的详细标准规范与细节。
本文是笔者对VTM代码的一点学习记录,成文于笔者刚开始接触VVC期间,期间很多概念和理论框架还很不成熟,若文中存在错误欢迎批评指正,也欢迎广大视频编码学习者沟通交流、共同进步。
VTM代码的下载及编译请参考博文:
【视频编码学习】H.266/VVC参考软件VTM配置运行(VTM-6.0版本)
本文涉及的代码主要存在于工程下的/lib/CommonLib/SourceFiles/IntraPrediction.cpp文件中。
一、MIP模式简述
MIP技术,即Matrix Weighted Intra Prediction,矩阵加权帧内预测技术,是VVC中的一种帧内预测技术。
该技术的设计源于最初对神经网络用于对参考像素进行处理从而实现帧内预测的设想。但直接运用神经网络对参考像素处理内存和时间的花费较大,故MIP技术使用脱机训练好的矩阵与下采样的参考像素向量相乘得到部分预测值,再通过上采样获得所有预测值。
简言之,MIP模式主要分为以下几个步骤。
1.参考像素下采样
MIP只能处理最小4×4,最大64×64的块。如上图所示,第一步是将左侧和上侧的参考像素进行下采样得到上图右侧的下采样结果,代码中是以向量的形式存储下采样结果。下采样的结果尺寸按照原块大小有如下表的规定。
mipSizeId | 块尺寸 | 下采样后边界长度(m_reducedBdrySize) |
---|---|---|
0 | 4×4 | 2 |
1 | 4×N、N×4、8×8 | 4 |
2 | 其余尺寸(最大64×64) | 4 |
下采样就是按照块大小指定的下采样后尺寸,对参考像素进行相应数量的参考像素取平均的操作。具体实现见本文代码注释。
2.矩阵运算
将下采样得到的参考像素按向量形式存放,根据Mip模式号索引与当前块对应的脱机训练好的矩阵A和偏移向量,将矩阵和下采样得到的参考像素向量相乘,相乘后的结果加上偏移向量得到最终运算结果。最终运算结果的尺寸和不同mipSizeId对应的矩阵A维度如下表所示。
mipSizeId | 块尺寸 | 下采样后边界长度(m_reducedBdrySize) | 矩阵A维度 | 矩阵乘法输出边界长度(m_reducedPredSize) |
---|---|---|---|---|
0 | 4×4 | 2 | 16×4 | 4 |
1 | 4×N、N×4、8×8 | 4 | 16×8 | 4 |
2 | 其余尺寸(最大64×64) | 4 | 64×7 | 8 |
如下图,矩阵运算的结果可以填充部分当前块的预测值,其他部分的预测值需要通过双线性插值完成。
3.上采样
如下图所示,通过先水平、再垂直的线性插值可以得到当前块所有点的预测值。线性插值的具体细节见下一篇博客的代码注释。
二、主要函数
本篇博客涉及的initIntraMip函数主要内容集中在Mip的第一步,即通过对当前块做相应尺寸的下采样并做相应处理获得第二步矩阵计算的输入向量。
1.函数代码与分析
(1)initIntraMip函数初始化Mip
void IntraPrediction::initIntraMip( const PredictionUnit &pu, const CompArea &area )
{
CHECK( area.width > MIP_MAX_WIDTH || area.height > MIP_MAX_HEIGHT, "Error: block size not supported for MIP" );
// prepare input (boundary) data for prediction准备输入数据(即【未滤波的】边界)进行预测
CHECK( m_ipaParam.refFilterFlag, "ERROR: unfiltered refs expected for MIP" );
//获取参考像素
Pel *ptrSrc = getPredictorPtr(area.compID);
//参考像素行的长度
const int srcStride = m_refBufferStride[area.compID];
const int srcHStride = 2;
//调用函数为MIP预测准备输入数据,即对参考像素下采样
m_matrixIntraPred.prepareInputForPred(CPelBuf(ptrSrc, srcStride, srcHStride), area,
pu.cu->slice->getSPS()->getBitDepth(toChannelType(area.compID)), area.compID);
}
(2)prepareInputForPred函数为MIP预测准备输入数据(即下采样)【入口在(1)】
void MatrixIntraPrediction::prepareInputForPred(const CPelBuf &pSrc, const Area &block, const int bitDepth,
const ComponentID compId)
{
m_component = compId;
// Step 1: Save block size and calculate dependent values保存块大小并计算MIP相关参数
initPredBlockParams(block);
// Step 2: Get the input data (left and top reference samples)获取输入数据(正上行和正左列参考像素)
m_refSamplesTop.resize(block.width);
//获取正上方参考像素
for (int x = 0; x < block.width; x++)
{
m_refSamplesTop[x] = pSrc.at(x + 1, 0);
}
//获取正左侧参考像素
m_refSamplesLeft.resize(block.height);
for (int y = 0; y < block.height; y++)
{
m_refSamplesLeft[y] = pSrc.at(y + 1, 1);
}
// Step 3: Compute the reduced boundary via Haar-downsampling (input for the prediction)通过Haar下采样计算缩减边界
//下采样后输入向量的尺寸为4或者8
const int inputSize = 2 * m_reducedBdrySize;
//不转置时的下采样结果向量,下采样先上后左
m_reducedBoundary .resize( inputSize );
//转置时的下采样结果向量,下采样先左后上
m_reducedBoundaryTransposed.resize( inputSize );
int* const topReduced = m_reducedBoundary.data();
//对上方参考像素进行下采样
boundaryDownsampling1D( topReduced, m_refSamplesTop.data(), block.width, m_reducedBdrySize );
int* const leftReduced = m_reducedBoundary.data() + m_reducedBdrySize;
//对左侧参考像素进行下采样
boundaryDownsampling1D( leftReduced, m_refSamplesLeft.data(), block.height, m_reducedBdrySize );
//转置的结果向量中先左侧结果再上方结果
int* const leftReducedTransposed = m_reducedBoundaryTransposed.data();
int* const topReducedTransposed = m_reducedBoundaryTransposed.data() + m_reducedBdrySize;
//循环将下采样结果放入转置向量对应位置
for( int x = 0; x < m_reducedBdrySize; x++ )
{
topReducedTransposed[x] = topReduced[x];
}
for( int y = 0; y < m_reducedBdrySize; y++ )
{
leftReducedTransposed[y] = leftReduced[y];
}
// Step 4: Rebase the reduced boundary缩小边界,推导矩阵乘法输入向量
m_inputOffset = m_reducedBoundary[0];
m_inputOffsetTransp = m_reducedBoundaryTransposed[0];
//sizeId<2时,第一位下采样参考像素设置为(最大值一半-原值)
//sizeId=2时,第一位下采样参考像素设置为0(后续用不到第一位)
const bool hasFirstCol = (m_sizeId < 2);
m_reducedBoundary [0] = hasFirstCol ? ((1 << (bitDepth - 1)) - m_inputOffset ) : 0;
m_reducedBoundaryTransposed[0] = hasFirstCol ? ((1 << (bitDepth - 1)) - m_inputOffsetTransp) : 0;
//循环遍历使之后的下采样参考像素为(原值-第一个下采样参考像素值)
for (int i = 1; i < inputSize; i++)
{
m_reducedBoundary [i] -= m_inputOffset;
m_reducedBoundaryTransposed[i] -= m_inputOffsetTransp;
}
}//ID=2,p[x]=pTemp[x+1] - pTemp[0] ID=0/1,p[0]=(1<<(BitDepth-1)) - pTemp[0],p[x]=pTemp[x]-pTemp[0] (x>0)
(3)initPredBlockParams函数准备边界长度、上采样因子等参数【入口在(2)】
void MatrixIntraPrediction::initPredBlockParams(const Size& block)
{
//获取当前块尺寸
m_blockSize = block;
// init size index根据块尺寸初始化SizeId
m_sizeId = getMipSizeId( m_blockSize );
// init reduced boundary size初始化缩减边界尺寸(下采样后的边界长度) 4*4的块缩减为宽高均为2,其他尺寸缩减为宽高均为4
//0代表4*4, 1代表4*N N*4 8*8, 2代表其他尺寸
m_reducedBdrySize = (m_sizeId == 0) ? 2 : 4;
// init reduced prediction size初始化缩减预测后的尺寸(矩阵乘法输出边界长度)
m_reducedPredSize = ( m_sizeId < 2 ) ? 4 : 8;
// init upsampling factors初始化上采样因子=块边长/矩阵乘法输出的边界长度
m_upsmpFactorHor = m_blockSize.width / m_reducedPredSize;
m_upsmpFactorVer = m_blockSize.height / m_reducedPredSize;
CHECKD( (m_upsmpFactorHor < 1) || ((m_upsmpFactorHor & (m_upsmpFactorHor - 1)) != 0), "Need power of two horizontal upsampling factor." );
CHECKD( (m_upsmpFactorVer < 1) || ((m_upsmpFactorVer & (m_upsmpFactorVer - 1)) != 0), "Need power of two vertical upsampling factor." );
}
(4)boundaryDownsampling1D函数进行下采样【入口在(2)】
void MatrixIntraPrediction::boundaryDownsampling1D(int* reducedDst, const int* const fullSrc, const SizeType srcLen, const SizeType dstLen)
{
//目标长度比原长度短时才使用下采样
if (dstLen < srcLen)
{
// Create reduced boundary by downsampling
//下采样因子=原长度/目标长度
const SizeType downsmpFactor = srcLen / dstLen;
//对数下采样因子
const int log2DownsmpFactor = floorLog2(downsmpFactor);
const int roundingOffset = (1 << (log2DownsmpFactor - 1));
SizeType srcIdx = 0;
//外层遍历逐个填充目标向量
for( SizeType dstIdx = 0; dstIdx < dstLen; dstIdx++ )
{
int sum = 0;
//内层遍历对当前目标点对应的下采样区域值求和
for( int k = 0; k < downsmpFactor; k++ )
{
sum += fullSrc[srcIdx++];
}
//求采样区域平均值放入结果对应位置
reducedDst[dstIdx] = (sum + roundingOffset) >> log2DownsmpFactor;
}
}
else
{
// Copy boundary if no downsampling is needed输出与输入尺寸一致时,直接复制,不进行下采样
for (SizeType i = 0; i < dstLen; ++i)
{
reducedDst[i] = fullSrc[i];
}
}
}
上一篇:H.266/VVC-VTM代码学习-帧内预测06-VVC的宽角度预测模式getModifiedWideAngle函数分析
下一篇:H.266/VVC-VTM代码学习-帧内预测08-MIP模式(2)predBlock函数完成Mip预测