在进行帧内预测前,首先要获取参考像素,并对参考像素进行滤波。
取当前要进行预测块的左一列和右一行,取出两倍宽和高的长度。如果都不存在,则用像素值512填充。
参考像素的图可见书P120。
如果左下或右上不存在,则用最左下或最右上的参考像素填充左下或右上部分。
VVC中,除去最左下角和最右上角的参考像素,满足模式相关条件(useFilteredIntraRefSamples),均使用1:2:1的滤波器进行滤波。
高层条件:色度预测不进行滤波,ISP和多参考行不滤波。
模式相关条件:DC不滤波,planar在块大于32时滤波,宽角度模式需要进行滤波。
以上过程对应xFillReferenceSamples,xFilterReferenceSamples,useFilteredIntraRefSamples三个函数
获取参考像素:
void IntraPrediction::xFillReferenceSamples( const CPelBuf &recoBuf, Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu )
{
/*************************************** 初始化 *****************************************/
const ChannelType chType = toChannelType( area.compID );
const CodingStructure &cs = *cu.cs;
const SPS &sps = *cs.sps;
const PreCalcValues &pcv = *cs.pcv;
const int multiRefIdx = (area.compID == COMPONENT_Y) ? cu.firstPU->multiRefIdx : 0;
const int tuWidth = area.width;
const int tuHeight = area.height;
const int predSize = m_topRefLength;
const int predHSize = m_leftRefLength;
#if JVET_M0102_INTRA_SUBPARTITIONS
const int cuWidth = cu.blocks[area.compID].width;
const int cuHeight = cu.blocks[area.compID].height;
const int whRatio = cu.ispMode && isLuma(area.compID) ? std::max(1, cuWidth / cuHeight) : std::max(1, tuWidth / tuHeight);
const int hwRatio = cu.ispMode && isLuma(area.compID) ? std::max(1, cuHeight / cuWidth) : std::max(1, tuHeight / tuWidth);
#else
int whRatio = std::max(1, tuWidth / tuHeight);
int hwRatio = std::max(1, tuHeight / tuWidth);
#endif
const int predStride = predSize + 1 + (whRatio + 1) * multiRefIdx;
const bool noShift = pcv.noChroma2x2 && area.width == 4; // don't shift on the lowest level (chroma not-split)
#if JVET_M0102_INTRA_SUBPARTITIONS
const int unitWidth = tuWidth <= 2 && cu.ispMode && isLuma(area.compID) ? tuWidth : pcv.minCUWidth >> (noShift ? 0 : getComponentScaleX(area.compID, sps.getChromaFormatIdc()));//宽度最小单位长度
const int unitHeight = tuHeight <= 2 && cu.ispMode && isLuma(area.compID) ? tuHeight : pcv.minCUHeight >> (noShift ? 0 : getComponentScaleY(area.compID, sps.getChromaFormatIdc()));//高度最小单位长度
#else
const int unitWidth = pcv.minCUWidth >> (noShift ? 0 : getComponentScaleX( area.compID, sps.getChromaFormatIdc() ));
const int unitHeight = pcv.minCUHeight >> (noShift ? 0 : getComponentScaleY( area.compID, sps.getChromaFormatIdc() ));
#endif
const int totalAboveUnits = (predSize + (unitWidth - 1)) / unitWidth; //整个上边有几个块
const int totalLeftUnits = (predHSize + (unitHeight - 1)) / unitHeight; //整个左边有几个块
const int totalUnits = totalAboveUnits + totalLeftUnits + 1; //+1 for top-left
const int numAboveUnits = std::max<int>( tuWidth / unitWidth, 1 ); //上边有几个块
const int numLeftUnits = std::max<int>( tuHeight / unitHeight, 1 ); //左边有几个块
const int numAboveRightUnits = totalAboveUnits - numAboveUnits; //右上有几个块
const int numLeftBelowUnits = totalLeftUnits - numLeftUnits; //左下有几个块
CHECK( numAboveUnits <= 0 || numLeftUnits <= 0 || numAboveRightUnits <= 0 || numLeftBelowUnits <= 0, "Size not supported" );
/*********************** 分析边界 **********************/
// ----- Step 1: analyze neighborhood -----
const Position posLT = area; //预测块内部的左上像素
const Position posRT = area.topRight(); //预测块内部的右上像素
const Position posLB = area.bottomLeft(); //预测块内部的左下像素
bool neighborFlags[4 * MAX_NUM_PART_IDXS_IN_CTU_WIDTH + 1];
int numIntraNeighbor = 0;//可用参考像素的个数
memset( neighborFlags, 0, totalUnits );
neighborFlags[totalLeftUnits] = isAboveLeftAvailable( cu, chType, posLT );
numIntraNeighbor += neighborFlags[totalLeftUnits] ? 1 : 0;
numIntraNeighbor += isAboveAvailable ( cu, chType, posLT, numAboveUnits, unitWidth, (neighborFlags + totalLeftUnits + 1) );
numIntraNeighbor += isAboveRightAvailable( cu, chType, posRT, numAboveRightUnits, unitWidth, (neighborFlags + totalLeftUnits + 1 + numAboveUnits) );
numIntraNeighbor += isLeftAvailable ( cu, chType, posLT, numLeftUnits, unitHeight, (neighborFlags + totalLeftUnits - 1) );
numIntraNeighbor += isBelowLeftAvailable ( cu, chType, posLB, numLeftBelowUnits, unitHeight, (neighborFlags + totalLeftUnits - 1 - numLeftUnits) );
/*********************** 填充参考像素 **********************/
// ----- Step 2: fill reference samples (depending on neighborhood) -----
CHECK((predHSize + 1) * predStride > m_iYuvExtSize, "Reference sample area not supported");
const Pel* srcBuf = recoBuf.buf; //图像像素原始buff
const int srcStride = recoBuf.stride;
Pel* ptrDst = refBufUnfiltered; //未滤波的参考像素
const Pel* ptrSrc;
const Pel valueDC = 1 << (sps.getBitDepth( chType ) - 1);
if( numIntraNeighbor == 0 )//参考像素都不可用(都位于边界),填充上像素512
{
// Fill border with DC value
for (int j = 0; j <= predSize + multiRefIdx; j++) { ptrDst[j] = valueDC; }
for (int i = 1; i <= predHSize + multiRefIdx; i++) { ptrDst[i*predStride] = valueDC; }
}
else if( numIntraNeighbor == totalUnits )//参考像素都可用(都不位于边界)
{
// Fill top-left border and top and top right with rec. samples
ptrSrc = srcBuf - (1 + multiRefIdx) * srcStride - (1 + multiRefIdx);
for (int j = 0; j <= predSize + multiRefIdx; j++) { ptrDst[j] = ptrSrc[j]; }
ptrSrc = srcBuf - multiRefIdx * srcStride - (1 + multiRefIdx);
for (int i = 1; i <= predHSize + multiRefIdx; i++) { ptrDst[i*predStride] = *(ptrSrc); ptrSrc += srcStride; }
}
else // reference samples are partially available 参考像素部分可用,填充上可用的部分
{
// Fill top-left sample(s) if available 填充左上像素
ptrSrc = srcBuf - (1 + multiRefIdx) * srcStride - (1 + multiRefIdx);
ptrDst = refBufUnfiltered;
if (neighborFlags[totalLeftUnits])
{
ptrDst[0] = ptrSrc[0];
for (int i = 1; i <= multiRefIdx; i++)
{
ptrDst[i] = ptrSrc[i];
ptrDst[i*predStride] = ptrSrc[i*srcStride];
}
}
// Fill left & below-left samples if available (downwards) 填充左侧和左下像素
ptrSrc += (1 + multiRefIdx) * srcStride;
ptrDst += (1 + multiRefIdx) * predStride;
for (int unitIdx = totalLeftUnits - 1; unitIdx > 0; unitIdx--)
{
if (neighborFlags[unitIdx])
{
for (int i = 0; i < unitHeight; i++)
{
ptrDst[i*predStride] = ptrSrc[i*srcStride];
}
}
ptrSrc += unitHeight * srcStride;
ptrDst += unitHeight * predStride;
}
// Fill last below-left sample(s) 填充最后的左下像素
if (neighborFlags[0])
{
int lastSample = (predHSize % unitHeight == 0) ? unitHeight : predHSize % unitHeight;
for (int i = 0; i < lastSample; i++)
{
ptrDst[i*predStride] = ptrSrc[i*srcStride];
}
}
// Fill above & above-right samples if available (left-to-right) 填充上侧和右上像素
ptrSrc = srcBuf - srcStride * (1 + multiRefIdx);
ptrDst = refBufUnfiltered + 1 + multiRefIdx;
for (int unitIdx = totalLeftUnits + 1; unitIdx < totalUnits - 1; unitIdx++)
{
if (neighborFlags[unitIdx])
{
for (int j = 0; j < unitWidth; j++)
{
ptrDst[j] = ptrSrc[j];
}
}
ptrSrc += unitWidth;
ptrDst += unitWidth;
}
// Fill last above-right sample(s) 填充最后的左下像素
if (neighborFlags[totalUnits - 1])
{
int lastSample = (predSize % unitWidth == 0) ? unitWidth : predSize % unitWidth;
for (int j = 0; j < lastSample; j++)
{
ptrDst[j] = ptrSrc[j];
}
}
// pad from first available down to the last below-left 填充所有可用的参考像素
ptrDst = refBufUnfiltered;
int lastAvailUnit = 0;
if (!neighborFlags[0])
{
int firstAvailUnit = 1;
while (firstAvailUnit < totalUnits && !neighborFlags[firstAvailUnit])
{
firstAvailUnit++;
}
// first available sample
int firstAvailRow = 0;
int firstAvailCol = 0;
if (firstAvailUnit < totalLeftUnits)
{
firstAvailRow = (totalLeftUnits - firstAvailUnit) * unitHeight + multiRefIdx;
}
else if (firstAvailUnit == totalLeftUnits)
{
firstAvailRow = multiRefIdx;
}
else
{
firstAvailCol = (firstAvailUnit - totalLeftUnits - 1) * unitWidth + 1 + multiRefIdx;
}
const Pel firstAvailSample = ptrDst[firstAvailCol + firstAvailRow * predStride];
// last sample below-left (n.a.)
int lastRow = predHSize + multiRefIdx;
// fill left column 填充左侧
for (int i = lastRow; i > firstAvailRow; i--)
{
ptrDst[i*predStride] = firstAvailSample;
}
// fill top row 填充上侧
if (firstAvailCol > 0)
{
for (int j = 0; j < firstAvailCol; j++)
{
ptrDst[j] = firstAvailSample;
}
}
lastAvailUnit = firstAvailUnit;
}
// pad all other reference samples. 填充其他参考像素,把不存在的参考像素填充上最边上的参考像素(万帅书P119)
int currUnit = lastAvailUnit + 1;
while (currUnit < totalUnits)
{
if (!neighborFlags[currUnit]) // samples not available
{
// last available sample
int lastAvailRow = 0;
int lastAvailCol = 0;
if (lastAvailUnit < totalLeftUnits)
{
lastAvailRow = (totalLeftUnits - lastAvailUnit - 1) * unitHeight + multiRefIdx + 1;
}
else if (lastAvailUnit == totalLeftUnits)
{
lastAvailCol = multiRefIdx;
}
else
{
lastAvailCol = (lastAvailUnit - totalLeftUnits) * unitWidth + multiRefIdx;
}
const Pel lastAvailSample = ptrDst[lastAvailCol + lastAvailRow * predStride];
// fill current unit with last available sample
if (currUnit < totalLeftUnits)
{
for (int i = lastAvailRow - 1; i >= lastAvailRow - unitHeight; i--)
{
ptrDst[i*predStride] = lastAvailSample;
}
}
else if (currUnit == totalLeftUnits)
{
for (int i = 1; i < multiRefIdx + 1; i++)
{
ptrDst[i*predStride] = lastAvailSample;
}
for (int j = 0; j < multiRefIdx + 1; j++)
{
ptrDst[j] = lastAvailSample;
}
}
else
{
int numSamplesInUnit = (currUnit == totalUnits - 1) ? ((predSize % unitWidth == 0) ? unitWidth : predSize % unitWidth) : unitWidth;
for (int j = lastAvailCol + 1; j <= lastAvailCol + numSamplesInUnit; j++)
{
ptrDst[j] = lastAvailSample;
}
}
}
lastAvailUnit = currUnit;
currUnit++;
}
}
// padding of extended samples above right with the last sample 填充多参考行部分的像素
int lastSample = multiRefIdx + predSize;
for (int j = 1; j <= whRatio * multiRefIdx; j++) { ptrDst[lastSample + j] = ptrDst[lastSample]; }
// padding of extended samples below left with the last sample 填充多参考行部分的像素
lastSample = multiRefIdx + predHSize;
for (int i = 1; i <= hwRatio * multiRefIdx; i++) { ptrDst[(lastSample + i)*predStride] = ptrDst[lastSample*predStride]; }
}
参考像素滤波:
void IntraPrediction::xFilterReferenceSamples( const Pel* refBufUnfiltered, Pel* refBufFiltered, const CompArea &area, const SPS &sps
, int multiRefIdx
)
{
/************************ 初始化 **************************/
if (area.compID != COMPONENT_Y)//色度只用一行参考
{
multiRefIdx = 0;
}
int whRatio = std::max(1, int(area.width / area.height));
int hwRatio = std::max(1, int(area.height / area.width));
const int predSize = m_topRefLength + (whRatio + 1) * multiRefIdx;
const int predHSize = m_leftRefLength + (hwRatio + 1) * multiRefIdx;
const int predStride = predSize + 1;
#if HEVC_USE_INTRA_SMOOTHING_T32 || HEVC_USE_INTRA_SMOOTHING_T64
// Strong intra smoothing
ChannelType chType = toChannelType( area.compID );
if( sps.getUseStrongIntraSmoothing() && isLuma( chType ) )
{
const Pel bottomLeft = refBufUnfiltered[predStride * predHSize];
const Pel topLeft = refBufUnfiltered[0];
const Pel topRight = refBufUnfiltered[predSize];
const int threshold = 1 << (sps.getBitDepth( chType ) - 5);
const bool bilinearLeft = abs( (bottomLeft + topLeft) - (2 * refBufUnfiltered[predStride * tuHeight]) ) < threshold; //difference between the
const bool bilinearAbove = abs( (topLeft + topRight) - (2 * refBufUnfiltered[ tuWidth ]) ) < threshold; //ends and the middle
if( tuWidth >= 32 && tuHeight >= 32 && bilinearLeft && bilinearAbove )
#if !HEVC_USE_INTRA_SMOOTHING_T32
if( tuWidth > 32 && tuHeight > 32 )
#endif
#if !HEVC_USE_INTRA_SMOOTHING_T64
if( tuWidth < 64 && tuHeight < 64 )
#endif
{
Pel *piDestPtr = refBufFiltered + (predStride * predHSize); // bottom left
// apply strong intra smoothing
for (int i = 0; i < predHSize; i++, piDestPtr -= predStride) //left column (bottom to top)
{
*piDestPtr = (((predHSize - i) * bottomLeft) + (i * topLeft) + predHSize / 2) / predHSize;
}
for( uint32_t i = 0; i <= predSize; i++, piDestPtr++ ) //full top row (left-to-right)
{
*piDestPtr = (((predSize - i) * topLeft) + (i * topRight) + predSize / 2) / predSize;
}
return;
}
}
#endif
// Regular reference sample filter
const Pel *piSrcPtr = refBufUnfiltered + (predStride * predHSize); // bottom left
Pel *piDestPtr = refBufFiltered + (predStride * predHSize); // bottom left
// bottom left (not filtered) 左下角,不进行滤波
*piDestPtr = *piSrcPtr;
piDestPtr -= predStride;
piSrcPtr -= predStride;
//left column (bottom to top) 左侧参考像素:下中上1:2:1
for( int i = 1; i < predHSize; i++, piDestPtr -= predStride, piSrcPtr -= predStride)
{
*piDestPtr = (piSrcPtr[predStride] + 2 * piSrcPtr[0] + piSrcPtr[-predStride] + 2) >> 2;
}
//top-left 左上角:下中右1:2:1
*piDestPtr = (piSrcPtr[predStride] + 2 * piSrcPtr[0] + piSrcPtr[1] + 2) >> 2;
piDestPtr++;
piSrcPtr++;
//top row (left-to-right) 上侧参考像素:左中右1:2:1
for( uint32_t i=1; i < predSize; i++, piDestPtr++, piSrcPtr++ )
{
*piDestPtr = (piSrcPtr[1] + 2 * piSrcPtr[0] + piSrcPtr[-1] + 2) >> 2;
}
// top right (not filtered) 右上角:不滤波
*piDestPtr=*piSrcPtr;
}
进行滤波条件:
bool IntraPrediction::useFilteredIntraRefSamples( const ComponentID &compID, const PredictionUnit &pu, bool modeSpecific, const UnitArea &tuArea )
{
const SPS &sps = *pu.cs->sps;
const ChannelType chType = toChannelType( compID );
/******************* 高层条件:亮度才进行滤波,ISP和多参考行不滤波 ********************/
// high level conditions
if( sps.getSpsRangeExtension().getIntraSmoothingDisabledFlag() ) { return false; }
if( !isLuma( chType ) && pu.chromaFormat != CHROMA_444 ) { return false; }
#if JVET_M0102_INTRA_SUBPARTITIONS
if( pu.cu->ispMode && isLuma(compID) ) { return false; }
#endif
if( !modeSpecific ) { return true; }
if (pu.multiRefIdx) { return false; }
/************* 模式相关条件:DC不滤波,planar在块大于32时滤波,宽角度滤波 **************/
// pred. mode related conditions
const int dirMode = PU::getFinalIntraMode( pu, chType );
int predMode = getWideAngle(tuArea.blocks[compID].width, tuArea.blocks[compID].height, dirMode);
if (predMode != dirMode ) { return true; }
if (dirMode == DC_IDX) { return false; }
if (dirMode == PLANAR_IDX)
{
return tuArea.blocks[compID].width * tuArea.blocks[compID].height > 32 ? true : false;
}
int diff = std::min<int>( abs( dirMode - HOR_IDX ), abs( dirMode - VER_IDX ) );
int log2Size = ((g_aucLog2[tuArea.blocks[compID].width] + g_aucLog2[tuArea.blocks[compID].height]) >> 1);
CHECK( log2Size >= MAX_INTRA_FILTER_DEPTHS, "Size not supported" );
return (diff > m_aucIntraFilter[chType][log2Size]);
}