参考像素平滑滤波 MDIS
- VVC 的帧内预测参考像素获取过程和 HEVC 相同,但参考像素滤波过程有所改进。在H.266中 MDIS(Mode Dependent Intra Smoothing)即模式依赖帧内平滑滤波,是对帧内预测的亮度分量参考像素进行滤波决策的一个技术。H266 对帧内角度模式使用 4 抽头插值滤波来提高预测精度;其提供了两种 4 抽头滤波方式,一种是基于 DCT 的插值滤波器(DCTIF)其生成方式和 HEVC 相同,另一种是 4 抽头平滑插值滤波器(SIF) 。
- 帧内角度模式分类:下图中实现是整数角度模式,虚线是分数角度模式
- A 类:水平或垂直(HOR_IDX、VER_IDX)
- B 类:角度是 non-fractional 方向,对角模式(2,34,66)以及部分的宽角度模式(-14,-12,-10,-6,72,76,78,80)
- C 类:其他角度模式
- MDIS 技术处理过程:
- 对于 A 类角度模式,不进行滤波处理,也不需要插值,直接用参考像素生成预测像素;
- 对于 B 类角度模式,满足下列五个条件,则使用 [1 2 1]/4 三抽头滤波器对参考像素进行滤波,将滤波后的参考像素直接作为预测像素;
- 参考行限制:预测过程为单行参考像素
- 大小限制:当前 CU 包含的像素个数大于 32
- 仅对亮度分量有用
- 不使用 ISP 模式
- 当前 CU 的预测模式是 Planar 模式或对角模式
- 对于 C 类角度模式和不满足上述条件的 B 类角度模式,会使用4抽头的高斯插值或者三次插值滤波器,对参考像素进行插值(对预测像素的参考像素在非整数位置的情况)生成预测像素。具体使用 DCTIF 插值还是 SIF 插值由下列条件决定:
-
设置 minDistVerHor =Min( Abs( predModeIntra − 50 ), Abs( predModeIntra − 18 ) )。
-
设置 nTbS=(Log2(W)+Log2(H))>>1。
-
intraHorVerDistThres[ nTbS ]设置如下,
-
如果 minDistVerHor 大于 intraHorVerDistThres[ nTbS ],使用 SIF 进行插值。否则使用 DCTIF 进行插值。
-
- 具体 DCTIF 和 SIF 的滤波系数可以参考标准或中文版书籍中介绍。
- VVenC 编码器中关于滤波器选择的函数在 IntraPrediction.cpp 文件中 initPredIntraParams 函数中判断。
// Function for initialization of intra prediction parameters
void IntraPrediction::initPredIntraParams(const CodingUnit& cu, const CompArea area, const SPS& sps)
{
const ComponentID compId = area.compID;
const ChannelType chType = toChannelType(compId);
const bool useISP = NOT_INTRA_SUBPARTITIONS != cu.ispMode && isLuma( chType );
const Size cuSize = Size( cu.blocks[compId].width, cu.blocks[compId].height );
const Size puSize = Size( area.width, area.height );
const Size& blockSize = useISP ? cuSize : puSize;
const int dirMode = CU::getFinalIntraMode(cu, chType);
const int predMode = getWideAngle( blockSize.width, blockSize.height, dirMode );
m_ipaParam.isModeVer = predMode >= DIA_IDX;
m_ipaParam.multiRefIndex = isLuma (chType) ? cu.multiRefIdx : 0 ;
m_ipaParam.refFilterFlag = false;
m_ipaParam.interpolationFlag = false;
m_ipaParam.applyPDPC = (puSize.width >= MIN_TB_SIZEY && puSize.height >= MIN_TB_SIZEY) && m_ipaParam.multiRefIndex == 0;
const int intraPredAngleMode = (m_ipaParam.isModeVer) ? predMode - VER_IDX : -(predMode - HOR_IDX);
int absAng = 0;
if (dirMode > DC_IDX && dirMode < NUM_LUMA_MODE) // intraPredAngle for directional modes
{
static const int angTable[32] = { 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 23, 26, 29, 32, 35, 39, 45, 51, 57, 64, 73, 86, 102, 128, 171, 256, 341, 512, 1024 };
static const int invAngTable[32] = {
0, 16384, 8192, 5461, 4096, 2731, 2048, 1638, 1365, 1170, 1024, 910, 819, 712, 630, 565,
512, 468, 420, 364, 321, 287, 256, 224, 191, 161, 128, 96, 64, 48, 32, 16
}; // (512 * 32) / Angle
const int absAngMode = abs(intraPredAngleMode);
const int signAng = intraPredAngleMode < 0 ? -1 : 1;
absAng = angTable [absAngMode];
m_ipaParam.absInvAngle = invAngTable[absAngMode];
m_ipaParam.intraPredAngle = signAng * absAng;
if (intraPredAngleMode < 0)
{
m_ipaParam.applyPDPC = false;
}
else if (intraPredAngleMode > 0)
{
const int sideSize = m_ipaParam.isModeVer ? puSize.height : puSize.width;
const int maxScale = 2;
m_ipaParam.angularScale = std::min(maxScale, floorLog2(sideSize) - (floorLog2(3 * m_ipaParam.absInvAngle - 2) - 8));
m_ipaParam.applyPDPC &= m_ipaParam.angularScale >= 0;
}
}
// high level conditions and DC intra prediction
if( !isLuma( chType )
|| useISP
|| CU::isMIP( cu, chType ) //th remove this
|| m_ipaParam.multiRefIndex
|| DC_IDX == dirMode
)
{
}
else if (cu.bdpcmM[chType])
{
m_ipaParam.refFilterFlag = false;
}
else if (dirMode == PLANAR_IDX) // Planar intra prediction
{
m_ipaParam.refFilterFlag = puSize.width * puSize.height > 32 ? true : false;
}
else if (!useISP)// HOR, VER and angular modes (MDIS)
{
bool filterFlag = false;
{
const int diff = std::min<int>( abs( predMode - HOR_IDX ), abs( predMode - VER_IDX ) );
const int log2Size = (Log2(puSize.width * puSize.height) >> 1);
CHECK( log2Size >= MAX_INTRA_FILTER_DEPTHS, "Size not supported" );
filterFlag = (diff > m_aucIntraFilter[log2Size]);
}
// Selelection of either ([1 2 1] / 4 ) refrence filter OR Gaussian 4-tap interpolation filter
if (filterFlag)
{
const bool isRefFilter = isIntegerSlope(absAng);
CHECK( puSize.width * puSize.height <= 32, "DCT-IF interpolation filter is always used for 4x4, 4x8, and 8x4 luma CB" );
m_ipaParam.refFilterFlag = isRefFilter;
m_ipaParam.interpolationFlag = !isRefFilter;
}
}
}
- VVenC 编码器中关于 [1 2 1]/4 三抽头滤波器的算法在 IntraPrediction.cpp 文件中 xFilterReferenceSamples 函数中计算。
void IntraPrediction::xFilterReferenceSamples( const Pel* refBufUnfiltered, Pel* refBufFiltered, const CompArea& area, const SPS &sps
, int multiRefIdx
, int stride
)
{
if (area.compID != COMP_Y)
{
multiRefIdx = 0;
}
const int predSize = m_topRefLength + multiRefIdx;
const int predHSize = m_leftRefLength + multiRefIdx;
const int predStride = stride == 0 ? predSize + 1 : stride;
const Pel topLeft =
(refBufUnfiltered[0] + refBufUnfiltered[1] + refBufUnfiltered[predStride] + refBufUnfiltered[predStride + 1] + 2)
>> 2;
refBufFiltered[0] = topLeft;
for (int i = 1; i < predSize; i++)
{
refBufFiltered[i] = (refBufUnfiltered[i - 1] + 2 * refBufUnfiltered[i] + refBufUnfiltered[i + 1] + 2) >> 2;
}
refBufFiltered[predSize] = refBufUnfiltered[predSize];
refBufFiltered += predStride;
refBufUnfiltered += predStride;
refBufFiltered[0] = topLeft;
for (int i = 1; i < predHSize; i++)
{
refBufFiltered[i] = (refBufUnfiltered[i - 1] + 2 * refBufUnfiltered[i] + refBufUnfiltered[i + 1] + 2) >> 2;
}
refBufFiltered[predHSize] = refBufUnfiltered[predHSize];
}
- VVenC 编码器中关于 4 抽头平滑滤波器的算法在 IntraPrediction.cpp 文件中 xPredIntraAng 函数中计算。
/** Function for deriving the simplified angular intra predictions.
*
* This function derives the prediction samples for the angular mode based on the prediction direction indicated by
* the prediction mode index. The prediction direction is given by the displacement of the bottom row of the block and
* the reference row above the block in the case of vertical prediction or displacement of the rightmost column
* of the block and reference column left from the block in the case of the horizontal prediction. The displacement
* is signalled at 1/32 pixel accuracy. When projection of the predicted pixel falls inbetween reference samples,
* the predicted value for the pixel is linearly interpolated from the reference samples. All reference samples are taken
* from the extended main reference.
*/
//NOTE: Bit-Limit - 25-bit source
void IntraPrediction::xPredIntraAng( PelBuf& pDst, const CPelBuf& pSrc, const ChannelType channelType, const ClpRng& clpRng)
{
int width =int(pDst.width);
int height=int(pDst.height);
const bool bIsModeVer = m_ipaParam.isModeVer;
const int multiRefIdx = m_ipaParam.multiRefIndex;
const int intraPredAngle = m_ipaParam.intraPredAngle;
const int absInvAngle = m_ipaParam.absInvAngle;
Pel* refMain;
Pel* refSide;
Pel refAbove[2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];
Pel refLeft [2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];
// Initialize the Main and Left reference array.
if (intraPredAngle < 0)
{
memcpy(&refAbove[height],pSrc.buf,(width + 2 + multiRefIdx)*sizeof(Pel));
for (int y = 0; y <= height + 1 + multiRefIdx; y++)
{
refLeft[y + width] = pSrc.at(y, 1);
}
refMain = bIsModeVer ? refAbove + height : refLeft + width;
refSide = bIsModeVer ? refLeft + width : refAbove + height;
// Extend the Main reference to the left.
int sizeSide = bIsModeVer ? height : width;
for (int k = -sizeSide; k <= -1; k++)
{
refMain[k] = refSide[std::min((-k * absInvAngle + 256) >> 9, sizeSide)];
}
}
else
{
memcpy(&refAbove[0], pSrc.buf, ((m_topRefLength)+multiRefIdx + 1) * sizeof(Pel));
for (int y = 0; y <= m_leftRefLength + multiRefIdx; y++)
{
refLeft[y] = pSrc.at(y, 1);
}
refMain = bIsModeVer ? refAbove : refLeft;
refSide = bIsModeVer ? refLeft : refAbove;
// Extend main reference to right using replication
const int log2Ratio = Log2(width) - Log2(height);
const int s = std::max<int>(0, bIsModeVer ? log2Ratio : -log2Ratio);
const int maxIndex = (multiRefIdx << s) + 2;
const int refLength = bIsModeVer ? m_topRefLength : m_leftRefLength;
const Pel val = refMain[refLength + multiRefIdx];
for (int z = 1; z <= maxIndex; z++)
{
refMain[refLength + multiRefIdx + z] = val;
}
}
// swap width/height if we are doing a horizontal mode:
if (!bIsModeVer)
{
std::swap(width, height);
}
Pel tempArray[MAX_CU_SIZE*MAX_CU_SIZE];
const int dstStride = bIsModeVer ? pDst.stride : MAX_CU_SIZE;
Pel* pDstBuf = bIsModeVer ? pDst.buf : tempArray;
// compensate for line offset in reference line buffers
refMain += multiRefIdx;
refSide += multiRefIdx;
Pel* pDsty = pDstBuf;
if( intraPredAngle == 0 ) // pure vertical or pure horizontal
{
if (m_ipaParam.applyPDPC)
{
const int scale = (Log2(width * height) - 2) >> 2;
IntraHorVerPDPC(pDsty,dstStride,refSide,width,height,scale,refMain,clpRng);
}
else
{
for( int y = 0; y < height; y++ )
{
memcpy(pDsty,&refMain[1],width*sizeof(Pel));
pDsty += dstStride;
}
}
}
else
{
if( !isIntegerSlope( abs( intraPredAngle ) ) )
{
int deltaPos = intraPredAngle * ( 1 + multiRefIdx );
if( isLuma( channelType ) )
{
if( width <= 2 )
{
for( int y = 0, deltaPos = intraPredAngle * ( 1 + multiRefIdx );
y < height;
y++, deltaPos += intraPredAngle, pDsty += dstStride )
{
const int deltaInt = deltaPos >> 5;
const int deltaFract = deltaPos & 31;
if( !isIntegerSlope( abs( intraPredAngle ) ) )
{
const bool useCubicFilter = !m_ipaParam.interpolationFlag;
const TFilterCoeff intraSmoothingFilter[4] = { TFilterCoeff( 16 - ( deltaFract >> 1 ) ),
TFilterCoeff( 32 - ( deltaFract >> 1 ) ),
TFilterCoeff( 16 + ( deltaFract >> 1 ) ),
TFilterCoeff( ( deltaFract >> 1 ) ) };
const TFilterCoeff* const f =
( useCubicFilter ) ? InterpolationFilter::getChromaFilterTable( deltaFract ) : intraSmoothingFilter;
for( int x = 0; x < width; x++ )
{
Pel p[4];
p[0] = refMain[deltaInt + x + 0];
p[1] = refMain[deltaInt + x + 1];
p[2] = refMain[deltaInt + x + 2];
p[3] = refMain[deltaInt + x + 3];
Pel val = ( f[0] * p[0] + f[1] * p[1] + f[2] * p[2] + f[3] * p[3] + 32 ) >> 6;
pDsty[x] = ClipPel( val, clpRng ); // always clip even though not always needed
}
}
}
}
else
{
IntraPredAngleLuma(pDstBuf, dstStride, refMain, width, height, deltaPos, intraPredAngle, nullptr, !m_ipaParam.interpolationFlag, clpRng);
}
}
else
{
IntraPredAngleChroma(pDstBuf,dstStride,refMain,width,height,deltaPos,intraPredAngle);
}
}
else
{
for (int y = 0, deltaPos = intraPredAngle * (1 + multiRefIdx); y<height; y++, deltaPos += intraPredAngle, pDsty += dstStride)
{
const int deltaInt = deltaPos >> 5;
// Just copy the integer samples
memcpy(pDsty,refMain + deltaInt + 1,width*sizeof(Pel));
}
}
if (m_ipaParam.applyPDPC)
{
pDsty = pDstBuf;
IntraAnglePDPC(pDsty,dstStride,refSide,width,height,m_ipaParam.angularScale,absInvAngle);
}
} // else
// Flip the block if this is the horizontal mode
if( !bIsModeVer )
{
pDst.transposedFrom( CPelBuf( pDstBuf, dstStride, width, height) );
}
}