H.266/VVC专栏传送
上一篇:H.266/VVC-VTM代码学习-帧内预测12-编码端获取MPM列表getIntraMPMs函数
下一篇:H.266/VVC-VTM代码学习-帧内预测14-ISP模式在estIntraPredLumaQT中的设定(2)
目录
- H.266/VVC专栏传送
- 前言
- 一、编码端确定ISP模式的相关代码
前言
VTM是H.266/VVC视频编码标准的参考软件,研究VTM代码给研究人员解释了VVC编码标准的详细标准规范与细节。
本文是笔者对VTM代码的一点学习记录,成文于笔者刚开始接触VVC期间,期间很多概念和理论框架还很不成熟,若文中存在错误欢迎批评指正,也欢迎广大视频编码学习者沟通交流、共同进步。
VTM代码的下载及编译请参考博文:
【视频编码学习】H.266/VVC参考软件VTM配置运行(VTM-6.0版本)
本文涉及的代码主要存在于工程下的Lib\EncoderLib\IntraSearch.cpp文件中。
一、编码端确定ISP模式的相关代码
1.estIntraPredLumaQT函数中确定是否测试ISP
//是否可用ISP(SPS确定使用ISP,且MTSFLAG为0(当前正在检查DCT-2变换),且lfnstIdx == 0(没有检查LFNST),且CU可以使用ISP(宽和高相乘要不小于16)
bool ispCanBeUsed = sps.getUseISP() && cu.mtsFlag == 0 && cu.lfnstIdx == 0 && CU::canUseISP(width, height, cu.cs->sps->getMaxTbSize());
//为ISP保存数据(使用ISP,且不启用ACT变换或者是第一种颜色空间)
bool saveDataForISP = ispCanBeUsed && (!colorTransformIsEnabled || isFirstColorSpace);
//测试ISP(使用ISP,且不启用ACT变换)
bool testISP = ispCanBeUsed && (!colorTransformIsEnabled || !cu.colorTransform);
① 只有当SPS中指定使用ISP,且mtsflag为0(即当前正在检查一次变换为DCT-2变换),且LFNST索引为0(即不使用LFNST),且CU可以使用ISP(当前块w*h>16,且尺寸不大于TU最大尺寸)时才能使用ISP。
② ISP可以使用,且不开启ACT变换或当前检查first color space则为ISP保存数据。
③ ISP可以使用,且不开启ACT变换或当前块不使用ACT变换时测试ISP。
2.estIntraPredLumaQT函数中初始化参数时确定ISP划分数量
//若测试ISP
if( testISP )
{
//reset the variables used for the tests
//重置ISP测试需要用到的变量
//用于存放选择的常规帧内模式
m_regIntraRDListWithCosts.clear();
//水平方向分块的总数
int numTotalPartsHor = (int)height >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_HORZ_SPLIT));
//垂直方向分块的总数
int numTotalPartsVer = (int)width >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_VERT_SPLIT));
//初始化m_ispTestedModes[0],即不使用LFNST时的ISP
//载入分块情况
m_ispTestedModes[0].init( numTotalPartsHor, numTotalPartsVer );
//the total number of subpartitions is modified to take into account the cases where LFNST cannot be combined with ISP due to size restrictions
//考虑到由于尺寸限制无法将LFNST与ISP组合的情况,修改子分区的总数
//即分块后块的尺寸应大于等于4
//水平分区数
numTotalPartsHor = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), HOR_INTRA_SUBPARTITIONS) ? numTotalPartsHor : 0;
//垂直分区数
numTotalPartsVer = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), VER_INTRA_SUBPARTITIONS) ? numTotalPartsVer : 0;
//NUM_LFNST_NUM_PER_SET = 3
//初始化m_ispTestedModes[1]和m_ispTestedModes[2],即使用LFNST时的ISP
//载入分块情况
for (int j = 1; j < NUM_LFNST_NUM_PER_SET; j++)
{
m_ispTestedModes[j].init(numTotalPartsHor, numTotalPartsVer);
}
}
代码中分别对不使用LFNST和使用LFNST的情况进行了区别处理(lfnstflag==0 or lfnstflag!=0)
① lfnstindex == 0,即不使用LFNST时:
//水平方向分块的总数
int numTotalPartsHor = (int)height >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_HORZ_SPLIT));
//垂直方向分块的总数
int numTotalPartsVer = (int)width >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_VERT_SPLIT));
代码中确定水平和垂直分区数的过程是先通过getISPSplitDim获得分块在对应维度上的尺寸,再用高或宽除以分块在对应维度的尺寸得到对应维度的分块数量。
其中,getISPSplitDim函数获得分块在对应维度上的尺寸。
uint32_t CU::getISPSplitDim( const int width, const int height, const PartSplit ispType )
{
//是否TU划分成多行
bool divideTuInRows = ispType == TU_1D_HORZ_SPLIT;
uint32_t splitDimensionSize, nonSplitDimensionSize, partitionSize, divShift = 2;
//划分成多行
if( divideTuInRows )
{
//划分方向的尺寸
splitDimensionSize = height;
//不划分方向的尺寸
nonSplitDimensionSize = width;
}
//划分成多列
else
{
//划分方向的尺寸
splitDimensionSize = width;
//不划分方向的尺寸
nonSplitDimensionSize = height;
}
//CU最小采样点数为16
const int minNumberOfSamplesPerCu = 1 << ( ( floorLog2(MIN_TB_SIZEY) << 1 ) );
//不划分侧尺寸小于16时为 16/不划分侧尺寸 ,否则为1
//此变量为满足最小采样点数的最少划分方向尺寸
const int factorToMinSamples = nonSplitDimensionSize < minNumberOfSamplesPerCu ? minNumberOfSamplesPerCu >> floorLog2(nonSplitDimensionSize) : 1;
//分块尺寸选择 splitDimensionSize/4(划分方向划分为4份) 和 factorToMinSamples 中较大者
//即最多在划分方向划分四份
partitionSize = ( splitDimensionSize >> divShift ) < factorToMinSamples ? factorToMinSamples : ( splitDimensionSize >> divShift );
CHECK( floorLog2(partitionSize) + floorLog2(nonSplitDimensionSize) < floorLog2(minNumberOfSamplesPerCu), "A partition has less than the minimum amount of samples!" );
return partitionSize;
}
由getISPSplitDim函数可见,ISP分块最多分4块,且必须保证每个分块的采样点数至少为16。
② lfnstindex != 0,即使用LFNST时:
//水平分区数
numTotalPartsHor = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), HOR_INTRA_SUBPARTITIONS) ? numTotalPartsHor : 0;
//垂直分区数
numTotalPartsVer = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), VER_INTRA_SUBPARTITIONS) ? numTotalPartsVer : 0;
使用LFNST时,需要重新判断ISP分块后的子块是否满足LFNST的尺寸要求,若使用LFNST且在ISP后满足LFNST的尺寸要求,则此时的ISP分块与不使用LFNST时相同。否则,此时ISP分块为0。
canUseLfnstWithISP用于判断ISP分块后子块是否满足LFNST尺寸要求。
bool CU::canUseLfnstWithISP( const CompArea& cuArea, const ISPType ispSplitType )
{
if( ispSplitType == NOT_INTRA_SUBPARTITIONS )
{
return false;
}
//ISP后子块尺寸
Size tuSize = ( ispSplitType == HOR_INTRA_SUBPARTITIONS ) ? Size( cuArea.width, CU::getISPSplitDim( cuArea.width, cuArea.height, TU_1D_HORZ_SPLIT ) ) :
Size( CU::getISPSplitDim( cuArea.width, cuArea.height, TU_1D_VERT_SPLIT ), cuArea.height );
//若分块后不满足宽高均大于4,则不可使用LFNST
if( !( tuSize.width >= MIN_TB_SIZEY && tuSize.height >= MIN_TB_SIZEY ) )
{
return false;
}
return true;
}
由canUseLfnstWithISP函数可见,若ISP分区后宽、高均大于4,则满足LFNST条件,将对应分区载入lfnstindex为1和2的情况。
3.estIntraPredLumaQT函数中第二轮角度模式选择后为ISP保存模式候选
//为之后测试ISP保存粗选出的传统帧内模式
if (saveDataForISP)
{
// we save the regular intra modes list
//保存常规帧内模式列表
m_ispCandListHor = uiRdModeList;
}
编码端第二轮角度模式检查后,若为ISP保存数据的标志位有效,则保存候选模式列表到ISP候选模式列表中。
4.estIntraPredLumaQT函数中在fastUDI对MPM的检查时将存在于MPM中的模式保存
//为ISP保存数据
if (saveDataForISP)
{
//we add the MPMs to the list that contains only regular intra modes
//将MPMs添加到仅包含常规帧内模式的列表中
//循环遍历MPM
for (int j = 0; j < numCand; j++)
{
bool mostProbableModeIncluded = false;
ModeInfo mostProbableMode(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiPreds[j]);
//遍历候选列表检查最可能模式是否包含
for (int i = 0; i < m_ispCandListHor.size(); i++)
{
mostProbableModeIncluded |= (mostProbableMode == m_ispCandListHor[i]);
}
//若当前最可能模式未被候选列表包含,则添加到候选列表中
if (!mostProbableModeIncluded)
{
m_ispCandListHor.push_back(mostProbableMode);
}
}
}
5.estIntraPredLumaQT函数中在细选RDcost前根据HAD候选列表长度对ISP候选模式长度缩减
if (saveDataForISP)//如果为ISP保存数据
{
//ISP候选列表长度设置为原长度和Had列表最大尺寸中的较小值
m_ispCandListHor.resize(std::min<size_t>(m_ispCandListHor.size(), maxSize));
}
6.estIntraPredLumaQT函数中在细选RDcost前记录非ISP模式数量并为ISP模式在候选模式列表预留位置
//非ISP模式的数量
int numNonISPModes = (int)uiRdModeList.size();
//若测试ISP
if ( testISP )
{
// we reserve positions for ISP in the common full RD list
//为ISP在通用的完整RD列表中保留位置
//ISP在RD列表中的最大模式数量(使用LFNST时:16*3,不使用LFNST:16)
const int maxNumRDModesISP = sps.getUseLFNST() ? 16 * NUM_LFNST_NUM_PER_SET : 16;
m_curIspLfnstIdx = 0;
//将ISP模式预留加入候选模式列表
for (int i = 0; i < maxNumRDModesISP; i++)
{
uiRdModeList.push_back( ModeInfo( false, false, 0, INTRA_SUBPARTITIONS_RESERVED, 0 ) );
}
}
上一篇:H.266/VVC-VTM代码学习-帧内预测12-编码端获取MPM列表getIntraMPMs函数
下一篇:H.266/VVC-VTM代码学习-帧内预测14-ISP模式在estIntraPredLumaQT中的设定(2)