帧内子划分 ISP
ISP 技术是在 JVET-2002-v3 提案中详细介绍其原理,在 VTM8 中完整展示算法。ISP是线基内预测(LIP)模式的更新版本,它改善了原始方法在编码增益和复杂度之间的权衡,ISP 算法的核心原理就是利用较近的像素点作为参考像素。 帧内子分区(ISP)根据块的大小,将亮度帧内预测块垂直或水平分割成2或4个子分区。例如,ISP的最小块大小是4x8(或8x4)。如果块大小大于4x8(或8x4),那么相应的块被4个子分区分割。 值得注意的是,M × 128(M ≤ 64)和128 × N(N ≤ 64)的 ISP 块可能会与64 × 64的 VDPU 产生潜在问题。例如,在单树情况下,一个M × 128的CU 包含一个 M x128 的亮度块 TB 和两个M/2 × 64的色度TB。如果CU 使用 ISP,那么亮度TB将被分割成四个M × 32的TB(只有水平分割是可能的),每个块都小于64 × 64。然而,在ISP色度块的当前设计中并没有被分割。因此,两个色度分量的尺寸都会大于32 × 32的块。类似地,使用ISP的128 × N的CU也可能会遇到类似的情况。因此,这两种情况对于64 × 64的 VDPU 来说是个问题。出于这个原因,能够使用ISP的 CU 尺寸被限制在最大64 × 64。图19展示了这两种可能性的例子。所有的子分区都满足至少有16个样本的条件。 在ISP中,不允许 1xN/2xN 子块预测依赖于之前解码的编码块的 1xN/2xN子 块的重建值,这样子块的最小预测宽度就变成了四个样本。例如,一个8xN(N > 4)的编码块,如果使用ISP进行垂直分割编码,将被分割成两个预测区域,每个区域的大小为4xN,并且有四个2xN的变换。此外,一个使用ISP进行垂直分割编码的4xN编码块,将使用完整的4xN块进行预测;四个1xN的变换被使用。尽管允许1xN和2xN的变换尺寸,但断言这些块在4xN区域内的变换可以并行执行。例如,当一个4xN预测区域包含四个1xN变换时,水平方向没有变换;垂直方向的变换可以作为单个4xN变换在垂直方向执行。同样地,当一个4xN预测区域包含两个2xN变换块时,两个2xN块在每个方向(水平和垂直)上的变换操作可以并行进行。因此,处理这些较小的块与处理4x4常规编码的帧内块相比,不会增加延迟。 对于每个子分区,通过将残差信号加到预测信号上来获得重建样本。这里,残差信号是通过熵解码、逆量化和逆变换等过程生成的。因此,重建样本值可用于生成下一个子分区的预测,每个子分区都会重复处理。此外,要处理的第一个子分区是包含CU左上角样本的子分区,然后继续向下(水平分割)或向右(垂直分割)。因此,用于生成子分区预测信号的参考样本仅位于行的左侧和上方。所有子分区共享相同的帧内模式。 ISP 技术与其他编码工具的交互:
多重参考线(MRL):如果一个块的MRL索引不是0,那么ISP编码模式将被推断为0,因此ISP模式信息不会发送到解码器。 熵编码系数组大小:熵编码子块的大小已经被修改,以便在所有可能的情况下它们都有16个样本,如表3-6所示。请注意,新的大小只影响由ISP产生的块,其中一个维度小于4个样本。在所有其他情况下,系数组保持4x4的尺寸。 CBF编码:假设至少有一个子分区具有非零的CBF。因此,如果n是子分区的数量,前n-1个子分区产生了零CBF,那么第n个子分区的CBF被推断为1。 MPM使用:在ISP模式编码的块中,MPM标志将被推断为1,MPM列表被修改以排除DC模式,并优先考虑ISP水平分割的水平内模式和垂直分割的垂直内模式。 变换大小限制:所有ISP变换,如果长度大于16点,使用DCT-II。 PDPC:当CU使用ISP编码模式时,PDPC滤波器不会应用于生成的子分区。 MTS标志:如果CU使用ISP编码模式,MTS CU标志将被设置为0,并且不会发送到解码器。因此,编码器不会对每个生成的子分区执行不同可用变换的RD测试。ISP模式的变换选择将根据内模式、处理顺序和使用的块大小固定和选择。因此,不需要信号。例如,让tH和tV分别是为 w×h 子分区选择的水平和垂直变换,其中w是宽度,h是高度。然后根据以下规则选择变换:
如果 w=1或 h=1,则没有水平或垂直变换。 如果 w=2 或 w>32,tH=DCT−II。 如果 h=2 或 h>32,tV=DCT−II。 否则,变换如表3-7所示选择。 在ISP模式下,允许所有67种内模式。如果相应的宽度和高度至少为4个样本,则应用PDPC。此外,内插滤波器选择的条件不再存在,并且在ISP模式下,对于分数位置插值,总是应用Cubic(DCT-IF)滤波器。 在论文 [ AN INTRA SUBPARTITION CODING MODE FOR VVC ] 中使用 VTM-3.0 官方参考代码中测试 ISP 技术的性能收益如下表: 在 VVenC 编码器中 UnitTools.cpp 文件中canUseISP 函数来判断是否可以使用 ISP 技术。
bool CU :: canUseISP ( const CodingUnit & cu, const ComponentID compID )
{
const int width = cu. blocks[ compID] . width;
const int height = cu. blocks[ compID] . height;
const int maxTrSize = cu. cs-> sps-> getMaxTbSize ( ) ;
return CU :: canUseISP ( width, height, maxTrSize ) ;
}
bool CU :: canUseISP ( const int width, const int height, const int maxTrSize )
{
bool notEnoughSamplesToSplit = ( Log2 ( width) + Log2 ( height) <= ( MIN_TB_LOG2_SIZEY << 1 ) ) ;
bool cuSizeLargerThanMaxTrSize = width > maxTrSize || height > maxTrSize;
if ( notEnoughSamplesToSplit || cuSizeLargerThanMaxTrSize )
{
return false ;
}
return true ;
}
在 VVenC 编码器中 IntraSearch.cpp 文件中estIntraPredLumaQT 函数中进行 ISP 初始化和遍历模式计算开启 ISP 和不开启 ISP 的代价,选择代价最小预测方式。
bool IntraSearch :: estIntraPredLumaQT ( CodingUnit & cu, Partitioner & partitioner, double bestCost)
{
CodingStructure & cs = * cu. cs;
const int width = partitioner. currArea ( ) . lwidth ( ) ;
const int height = partitioner. currArea ( ) . lheight ( ) ;
const TempCtx ctxStart ( m_CtxCache, m_CABACEstimator-> getCtx ( ) ) ;
double costInterCU = xFindInterCUCost ( cu ) ;
bool validReturn = false ;
int numModesAvailable = NUM_LUMA_MODE;
static_vector< ModeInfo, FAST_UDI_MAX_RDMODE_NUM> RdModeList;
static_vector< ModeInfo, FAST_UDI_MAX_RDMODE_NUM> HadModeList;
static_vector< double , FAST_UDI_MAX_RDMODE_NUM> CandCostList;
static_vector< double , FAST_UDI_MAX_RDMODE_NUM> CandHadList;
int numModesForFullRD = g_aucIntraModeNumFast_UseMPM_2D[ Log2 ( width) - MIN_CU_LOG2] [ Log2 ( height) - MIN_CU_LOG2] ;
if ( m_pcEncCfg-> m_numIntraModesFullRD > 0 )
numModesForFullRD= m_pcEncCfg-> m_numIntraModesFullRD;
# if INTRA_FULL_SEARCH
numModesForFullRD = numModesAvailable;
# endif
const SPS& sps = * cu. cs-> sps;
const bool mipAllowed = sps. MIP && cu. lwidth ( ) <= sps. getMaxTbSize ( ) && cu. lheight ( ) <= sps. getMaxTbSize ( ) && ( ( cu. lfnstIdx == 0 ) || allowLfnstWithMip ( cu. lumaSize ( ) ) ) ;
const int SizeThr = 8 >> std:: max ( 0 , m_pcEncCfg-> m_useFastMIP - 1 ) ;
const bool testMip = mipAllowed && ( cu. lwidth ( ) <= ( SizeThr * cu. lheight ( ) ) && cu. lheight ( ) <= ( SizeThr * cu. lwidth ( ) ) ) && ( cu. lwidth ( ) <= MIP_MAX_WIDTH && cu. lheight ( ) <= MIP_MAX_HEIGHT ) ;
bool testISP = sps. ISP && CU :: canUseISP ( width, height, cu. cs-> sps-> getMaxTbSize ( ) ) ;
if ( testISP)
{
int numTotalPartsHor = ( int ) width >> floorLog2 ( CU :: getISPSplitDim ( width, height, TU_1D_VERT_SPLIT) ) ;
int numTotalPartsVer = ( int ) height >> floorLog2 ( CU :: getISPSplitDim ( width, height, TU_1D_HORZ_SPLIT) ) ;
m_ispTestedModes[ 0 ] . init ( numTotalPartsHor, numTotalPartsVer, 0 ) ;
numTotalPartsHor = sps. LFNST && CU :: canUseLfnstWithISP ( cu. Y ( ) , HOR_INTRA_SUBPARTITIONS) ? numTotalPartsHor : 0 ;
numTotalPartsVer = sps. LFNST && CU :: canUseLfnstWithISP ( cu. Y ( ) , VER_INTRA_SUBPARTITIONS) ? numTotalPartsVer : 0 ;
for ( int j = 1 ; j < NUM_LFNST_NUM_PER_SET; j++ )
{
m_ispTestedModes[ j] . init ( numTotalPartsHor, numTotalPartsVer, 0 ) ;
}
testISP = m_ispTestedModes[ 0 ] . numTotalParts[ 0 ] ;
}
else
{
m_ispTestedModes[ 0 ] . init ( 0 , 0 , 0 ) ;
}
xEstimateLumaRdModeList ( numModesForFullRD, RdModeList, HadModeList, CandCostList, CandHadList, cu, testMip) ;
CHECK ( ( size_t) numModesForFullRD != RdModeList. size ( ) , "Inconsistent state!" ) ;
if ( m_pcEncCfg-> m_usePbIntraFast && ! cs. slice-> isIntra ( ) && RdModeList. size ( ) < numModesAvailable )
{
double pbintraRatio = m_pcEncCfg-> m_usePbIntraFast == 1 && ( cs. area. lwidth ( ) >= 16 && cs. area. lheight ( ) >= 16 ) ? 1.2 : PBINTRA_RATIO;
int maxSize = - 1 ;
ModeInfo bestMipMode;
int bestMipIdx = - 1 ;
for ( int idx = 0 ; idx < RdModeList. size ( ) ; idx++ )
{
if ( RdModeList[ idx] . mipFlg )
{
bestMipMode = RdModeList[ idx] ;
bestMipIdx = idx;
break ;
}
}
const int numHadCand = 3 ;
for ( int k = numHadCand - 1 ; k >= 0 ; k-- )
{
if ( CandHadList. size ( ) < ( k + 1 ) || CandHadList[ k] > cs. interHad * pbintraRatio) { maxSize = k; }
}
if ( maxSize > 0 )
{
RdModeList. resize ( std:: min < size_t> ( RdModeList. size ( ) , maxSize) ) ;
if ( bestMipIdx >= 0 )
{
if ( RdModeList. size ( ) <= bestMipIdx )
{
RdModeList. push_back ( bestMipMode) ;
m_SortedPelUnitBufs-> swap ( maxSize, bestMipIdx ) ;
}
}
}
if ( maxSize == 0 )
{
cs. dist = MAX_DISTORTION;
cs. interHad = 0 ;
return false ;
}
}
ModeInfo bestPUMode;
CodingStructure * csTemp = m_pTempCS;
CodingStructure * csBest = m_pBestCS;
csTemp-> slice = csBest-> slice = cs. slice;
csTemp-> picture = csBest-> picture = cs. picture;
csTemp-> compactResize ( cu ) ;
csBest-> compactResize ( cu ) ;
csTemp-> initStructData ( ) ;
csBest-> initStructData ( ) ;
int bestLfnstIdx = 0 ;
const bool useBDPCM = cs. picture-> useBDPCM;
int NumBDPCMCand = ( useBDPCM && sps. BDPCM && CU :: bdpcmAllowed ( cu, ComponentID ( partitioner. chType) ) ) ? 2 : 0 ;
int bestbdpcmMode = 0 ;
int bestISP = 0 ;
int bestMrl = 0 ;
bool bestMip = 0 ;
int EndMode = ( int ) RdModeList. size ( ) ;
bool useISPlfnst = testISP && sps. LFNST;
bool noLFNST_ts = false ;
double bestCostIsp[ 2 ] = { MAX_DOUBLE, MAX_DOUBLE } ;
bool disableMTS = false ;
bool disableLFNST = false ;
bool disableDCT2test = false ;
if ( m_pcEncCfg-> m_FastIntraTools)
{
int speedIntra = 0 ;
xSpeedUpIntra ( bestCost, EndMode, speedIntra, cu) ;
disableMTS = ( speedIntra >> 2 ) & 0x1 ;
disableLFNST = ( speedIntra >> 1 ) & 0x1 ;
disableDCT2test = speedIntra>> 3 ;
if ( disableLFNST)
{
noLFNST_ts = true ;
useISPlfnst = false ;
}
if ( speedIntra & 0x1 )
{
testISP = false ;
}
}
for ( int mode_cur = 0 ; mode_cur < EndMode + NumBDPCMCand; mode_cur++ )
{
int mode = mode_cur;
if ( mode_cur >= EndMode)
{
mode = mode_cur - EndMode ? - 1 : - 2 ;
testISP = false ;
}
ModeInfo testMode;
int noISP = 0 ;
int endISP = testISP ? 2 : 0 ;
bool noLFNST = false || noLFNST_ts;
if ( mode && useISPlfnst)
{
noLFNST |= ( bestCostIsp[ 0 ] > ( bestCostIsp[ 1 ] * 1.4 ) ) ;
if ( mode > 2 )
{
endISP = 0 ;
testISP = false ;
}
}
if ( testISP)
{
xSpeedUpISP ( 1 , testISP, mode, noISP, endISP, cu, RdModeList, bestPUMode, bestISP, bestLfnstIdx) ;
}
int startISP = 0 ;
if ( disableDCT2test && mode && bestISP)
{
startISP = endISP ? 1 : 0 ;
}
for ( int ispM = startISP; ispM <= endISP; ispM++ )
{
if ( ispM && ( ispM == noISP) )
{
continue ;
}
if ( mode < 0 )
{
cu. bdpcmM[ CH_L] = - mode;
testMode = ModeInfo ( false , false , 0 , NOT_INTRA_SUBPARTITIONS, cu. bdpcmM[ CH_L] == 2 ? VER_IDX : HOR_IDX) ;
}
else
{
testMode = RdModeList[ mode] ;
cu. bdpcmM[ CH_L] = 0 ;
}
cu. ispMode = ispM;
cu. mipFlag = testMode. mipFlg;
cu. mipTransposedFlag = testMode. mipTrFlg;
cu. multiRefIdx = testMode. mRefId;
cu. intraDir[ CH_L] = testMode. modeId;
if ( cu. ispMode && xSpeedUpISP ( 0 , testISP, mode, noISP, endISP, cu, RdModeList, bestPUMode, bestISP, 0 ) )
{
continue ;
}
if ( m_pcEncCfg-> m_FastIntraTools && ( cu. ispMode || sps. LFNST || sps. MTS) )
{
m_ispTestedModes[ 0 ] . intraWasTested = true ;
}
CHECK ( cu. mipFlag && cu. multiRefIdx, "Error: combination of MIP and MRL not supported" ) ;
CHECK ( cu. multiRefIdx && ( cu. intraDir[ 0 ] == PLANAR_IDX) , "Error: combination of MRL and Planar mode not supported" ) ;
CHECK ( cu. ispMode && cu. mipFlag, "Error: combination of ISP and MIP not supported" ) ;
CHECK ( cu. ispMode && cu. multiRefIdx, "Error: combination of ISP and MRL not supported" ) ;
cs. initSubStructure ( * csTemp, partitioner. chType, cs. area, true ) ;
int doISP = ( ( ( cu. ispMode == 0 ) && noLFNST) || ( useISPlfnst && mode && cu. ispMode && ( bestLfnstIdx == 0 ) ) || disableLFNST) ? - mode : mode;
xIntraCodingLumaQT ( * csTemp, partitioner, m_SortedPelUnitBufs-> getBufFromSortedList ( mode) , bestCost, doISP, disableMTS) ;
DTRACE ( g_trace_ctx, D_INTRA_COST, "IntraCost T [x=%d,y=%d,w=%d,h=%d] %f (%d,%d,%d,%d,%d,%d) \n" , cu. blocks[ 0 ] . x,
cu. blocks[ 0 ] . y, width, height, csTemp-> cost, testMode. modeId, testMode. ispMod,
cu. multiRefIdx, cu. mipFlag, cu. lfnstIdx, cu. mtsFlag) ;
if ( cu. ispMode && ! csTemp-> cus[ 0 ] -> firstTU-> cbf[ COMP_Y] )
{
csTemp-> cost = MAX_DOUBLE;
csTemp-> costDbOffset = 0 ;
}
if ( useISPlfnst)
{
int n = ( cu. ispMode == 0 ) ? 0 : 1 ;
bestCostIsp[ n] = csTemp-> cost < bestCostIsp[ n] ? csTemp-> cost : bestCostIsp[ n] ;
}
if ( csTemp-> cost < csBest-> cost)
{
validReturn = true ;
std:: swap ( csTemp, csBest) ;
bestPUMode = testMode;
bestLfnstIdx = csBest-> cus[ 0 ] -> lfnstIdx;
bestISP = csBest-> cus[ 0 ] -> ispMode;
bestMip = csBest-> cus[ 0 ] -> mipFlag;
bestMrl = csBest-> cus[ 0 ] -> multiRefIdx;
bestbdpcmMode = cu. bdpcmM[ CH_L] ;
m_ispTestedModes[ bestLfnstIdx] . bestSplitSoFar = ISPType ( bestISP) ;
if ( csBest-> cost < bestCost)
{
bestCost = csBest-> cost;
}
if ( ( csBest-> getTU ( partitioner. chType) -> mtsIdx[ COMP_Y] == MTS_SKIP) && ( floorLog2 ( csBest-> getTU ( partitioner. chType) -> blocks[ COMP_Y] . area ( ) ) >= 6 ) )
{
noLFNST_ts = 1 ;
}
}
m_CABACEstimator-> getCtx ( ) = ctxStart;
csTemp-> releaseIntermediateData ( ) ;
if ( m_pcEncCfg-> m_fastLocalDualTreeMode && CU :: isConsIntra ( cu) && ! cu. slice-> isIntra ( ) && csBest-> cost != MAX_DOUBLE && costInterCU != COST_UNKNOWN && mode >= 0 )
{
if ( ( m_pcEncCfg-> m_fastLocalDualTreeMode == 2 ) || ( csBest-> cost > costInterCU * 1.5 ) )
{
EndMode = 0 ;
break ;
}
}
}
}
if ( m_pcEncCfg-> m_FastIntraTools && ( sps. ISP|| sps. LFNST || sps. MTS) )
{
int bestMode = csBest-> getTU ( partitioner. chType) -> mtsIdx[ COMP_Y] ? 4 : 0 ;
bestMode |= bestLfnstIdx ? 2 : 0 ;
bestMode |= bestISP ? 1 : 0 ;
m_ispTestedModes[ 0 ] . bestIntraMode = bestMode;
}
cu. ispMode = bestISP;
if ( validReturn )
{
cs. useSubStructure ( * csBest, partitioner. chType, TREE_D, cu. singleChan ( CH_L ) , true ) ;
const ReshapeData& reshapeData = cs. picture-> reshapeData;
if ( cs. picHeader-> lmcsEnabled && reshapeData. getCTUFlag ( ) )
{
cs. getRspRecoBuf ( ) . copyFrom ( csBest-> getRspRecoBuf ( ) ) ;
}
cu. lfnstIdx = bestLfnstIdx;
cu. mipTransposedFlag = bestPUMode. mipTrFlg;
cu. intraDir[ CH_L] = bestPUMode. modeId;
cu. bdpcmM[ CH_L] = bestbdpcmMode;
cu. mipFlag = bestMip;
cu. multiRefIdx = bestMrl;
}
else
{
THROW ( "fix this" ) ;
}
csBest-> releaseIntermediateData ( ) ;
return validReturn;
}