initIntraPatternChType函数在帧内预测的过程中会被调用。
initIntraPatternChType用来准备好帧内预测过程中需要用到的当前pu左侧和上侧的参考像素。
Void IntraPrediction::initIntraPatternChType(const CodingUnit &cu, const CompArea &area, const Bool bFilterRefSamples)
{
const CodingStructure& cs = *cu.cs;
Pel *refBufUnfiltered = m_piYuvExt[area.compID][PRED_BUF_UNFILTERED];//获取到的未滤波参考像素
Pel *refBufFiltered = m_piYuvExt[area.compID][PRED_BUF_FILTERED]; //滤波后的参考像素
// ----- Step 1: unfiltered reference samples ----- //由重建像素获取pu的参考像素
xFillReferenceSamples( cs.picture->getRecoBuf( area ), refBufUnfiltered, area, cu ); //获取当前pu的参考像素
// ----- Step 2: filtered reference samples -----
if( bFilterRefSamples ) //参考像素是否滤波
{
xFilterReferenceSamples( refBufUnfiltered, refBufFiltered, area, *cs.sps ); //对获取到的没有滤波的参考像素进行滤波
}
}
xFillReferenceSamples函数填充帧内预测过程中左侧和上侧的参考像素模板。
参考像素模板为当前pu的左侧、左下、上侧、右上、左上的像素信息,其中左下和右上的参考像素模板长度分别为pu.width和pu.hight,即左和上的参考像素长度,都为width+height。左上的那个参考像素,可以看做归并到上面的参考像素,长度为unitWidth,函数中有体现。
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 tuWidth = area.width;
const int tuHeight = area.height;
const int predSize = tuWidth + tuHeight;
const int predStride = predSize + 1; //参考像素的stride,为tuWidth+tuHeight+1,因为多了左侧一列
const bool noShift = pcv.noChroma2x2 && area.width == 4; // don't shift on the lowest level (chroma not-split)
const int unitWidth = pcv.minCUWidth >> (noShift ? 0 : getComponentScaleX( area.compID, sps.getChromaFormatIdc() ));//VTM处理的最小块大小
const int unitHeight = pcv.minCUHeight >> (noShift ? 0 : getComponentScaleY( area.compID, sps.getChromaFormatIdc() ));//亮度4x4,色度2x2
const int totalAboveUnits = (predSize + (unitWidth - 1)) / unitWidth; //上侧参考像素可以划分的最小块数目
const int totalLeftUnits = (predSize + (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]; //用来标记参考像素各位置的pu是否可用,起点左下第一个块,终点右上最后一个块
int numIntraNeighbor = 0; //参考像素处的可用小块数目
memset( neighborFlags, 0, totalUnits ); //初始化为参考处的pu都不可用
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) );
//以上的各isAvailable函数中,最后一个参数用来记录标记各个位置处最小块是否可用
// ----- Step 2: fill reference samples (depending on neighborhood) -----
CHECK( predStride * predStride > m_iYuvExtSize, "Reference sample area not supported" );
const Pel* srcBuf = recoBuf.buf; //参考像素,即cs的重建像素
const int srcStride = recoBuf.stride; //stride一般为整个picture的width
Pel* ptrDst = refBufUnfiltered; //计算存储参考像素
const Pel* ptrSrc;
const Pel valueDC = 1 << (sps.getBitDepth( chType ) - 1); //default填充像素值
if( numIntraNeighbor == 0 ) //如果参考像素位置没有一个小块可用
{
// Fill border with DC value
for( int j = 0; j <= predSize; j++ ) { ptrDst[j] = valueDC; }
for( int i = 1; i <= predSize; i++ ) { ptrDst[i*predStride] = valueDC; } //参考像素全部填充default值
}
else if( numIntraNeighbor == totalUnits ) //如果参考像素位置所有的小块都可用,参考像素直接copy
{
// Fill top-left border and top and top right with rec. samples
ptrSrc = srcBuf - srcStride - 1;
for( int j = 0; j <= predSize; j++ ) { ptrDst[j] = ptrSrc[j]; } //左上、上、右上区域的copy
// Fill left and below left border with rec. samples
ptrSrc = srcBuf - 1;
for( int i = 1; i <= predSize; i++ ) { ptrDst[i*predStride] = *(ptrSrc); ptrSrc += srcStride; } //左、左下区域的copy
}
else // reference samples are partially available //参考像素部分可用,那么由可用的值填充没有用的值
{
// BB: old implementation using tmpLineBuf
// ---------------------------------------
Pel tmpLineBuf[5 * MAX_CU_SIZE]; //一维的参考像素存储,方便padding操作
Pel* ptrTmp;
int unitIdx;
// Initialize
const int totalSamples = (totalLeftUnits * unitHeight) + ((totalAboveUnits + 1) * unitWidth); // all above units have "unitWidth" samples each, all left/below-left units have "unitHeight" samples each
for( int k = 0; k < totalSamples; k++ ) { tmpLineBuf[k] = valueDC; }
// Fill top-left sample
ptrSrc = srcBuf - srcStride - 1; //对可用的参考像素信息直接copy,先不用notAvailable的像素
ptrTmp = tmpLineBuf + (totalLeftUnits * unitHeight);
unitIdx = totalLeftUnits;
if( neighborFlags[unitIdx] )
{
Pel topLeftVal = ptrSrc[0];
for( int j = 0; j < unitWidth; j++ ) { ptrTmp[j] = topLeftVal; } //左上可用时copy
}
// Fill left & below-left samples (downwards)
ptrSrc += srcStride;
ptrTmp--;
unitIdx--;
for( int k = 0; k < totalLeftUnits; k++ )
{
if( neighborFlags[unitIdx] ) //左边可用像素copy
{
for( int i = 0; i < unitHeight; i++ ) { ptrTmp[-i] = ptrSrc[i*srcStride]; }
}
ptrSrc += unitHeight*srcStride;
ptrTmp -= unitHeight;
unitIdx--;
}
// Fill above & above-right samples (left-to-right) (each unit has "unitWidth" samples)
ptrSrc = srcBuf - srcStride;
ptrTmp = tmpLineBuf + (totalLeftUnits * unitHeight) + unitWidth; // offset line buffer by totalLeftUnits*unitHeight (for left/below-left) + unitWidth (for above-left)
unitIdx = totalLeftUnits + 1;
for( int k = 0; k < totalAboveUnits; k++ )
{
if( neighborFlags[unitIdx] )
{
for( int j = 0; j < unitWidth; j++ ) { ptrTmp[j] = ptrSrc[j]; } //上边可用像素copy
}
ptrSrc += unitWidth;
ptrTmp += unitWidth;
unitIdx++;
}
// Pad reference samples when necessary //padding操作,用可用像素补全notAvailable的像素
int currUnit = 0;
Pel* ptrTmpCurrUnit = tmpLineBuf;
if( !neighborFlags[0] ) //如果起点开始就不可用,那么一直查找到可用点,用可用点像素补全前面的所有像素
{
int nextUnit = 1;
while( nextUnit < totalUnits && !neighborFlags[nextUnit] ) //查找起点开始第一个有用的点
{
nextUnit++;
}
Pel* ptrTmpRef = tmpLineBuf + ((nextUnit < totalLeftUnits) ? (nextUnit * unitHeight) : ((totalLeftUnits * (unitHeight - unitWidth)) + (nextUnit * unitWidth)));
const Pel refSample = *ptrTmpRef;
// Pad unavailable samples with new value
// fill left column
while( currUnit < std::min<int>( nextUnit, totalLeftUnits ) )
{
for( int i = 0; i < unitHeight; i++ ) { ptrTmpCurrUnit[i] = refSample; }
ptrTmpCurrUnit += unitHeight;
currUnit++;
}
// fill top row
while( currUnit < nextUnit ) //如果第一个可用点一直查找到上边了,还要补全上面的一些点像素
{
for( int j = 0; j < unitWidth; j++ ) { ptrTmpCurrUnit[j] = refSample; }
ptrTmpCurrUnit += unitWidth;
currUnit++;
}
}
// pad all other reference samples.
while( currUnit < totalUnits ) //如果有一点不可用,用它前面的可用点补全
{
const int numSamplesInCurrUnit = (currUnit >= totalLeftUnits) ? unitWidth : unitHeight;
if( !neighborFlags[currUnit] ) // samples not available
{
const Pel refSample = *(ptrTmpCurrUnit - 1);
for( int k = 0; k < numSamplesInCurrUnit; k++ ) { ptrTmpCurrUnit[k] = refSample; }
}
ptrTmpCurrUnit += numSamplesInCurrUnit;
currUnit++;
}
// Copy processed samples //获取到的参考像素模板,存储在tmpLineBuf中,现在copy到refBufUnfiltered中返回,用于接下来的滤波或者预测
ptrTmp = tmpLineBuf + (totalLeftUnits * unitHeight) + (unitWidth - 1);
for( int j = 0; j <= predSize; j++ ) { ptrDst[j] = ptrTmp[j]; } // top left, top and top right samples
ptrTmp = tmpLineBuf + (totalLeftUnits * unitHeight);
for( int i = 1; i <= predSize; i++ ) { ptrDst[i*predStride] = ptrTmp[-i]; }
}
}