xFillReferenceSamples()函数是参考像素的获取过程。
主要步骤:
1、分析邻近的像素是否可获取
2、进行参考样本的填充:若临近的像素全部可获取,则赋值;全部不可获取,则赋默认值;若部分可获取,则对可获取的赋对应的值,不可获取的用默认值填充:
3、复制样本。
注:
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;
//帧内预测中,是以unit为单位进行的,宽高的unit计算如下
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() ));
const int unitHeight = pcv.minCUHeight >> (noShift ? 0 : getComponentScaleY( area.compID, sps.getChromaFormatIdc() ));
//totalAboveUnits是上方全部参考单元数量(上方+左侧个数和),即全部参考像素数量对unitWidth做四舍五入处理得到的。totalLeftUnits同理。totalUnits在二者的基础上+1,包括了左上角的unit,即为总的参考unit数量。
const int totalAboveUnits = (predSize + (unitWidth - 1)) / unitWidth;
const int totalLeftUnits = (predSize + (unitHeight - 1)) / unitHeight;
const int totalUnits = totalAboveUnits + totalLeftUnits + 1; //+1 for top-left
//这里将totalAboveUnits分为numAboveUnits以及numAboveRightUnits,左侧的同理。
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();
//neighborFlags标记着当前unit是否可获取,在isxxxxAvailable()函数中赋值。numIntraNeighbor表示全部的可获取unit数量。
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;
//观察输入参数:
//isAboveAvailable函数最后一个参数 (neighborFlags + totalLeftUnits + 1)即为其在neighborFlags标志位开始的位置。函数中按照unitWidth循环检测所在的CU是否可获取,可获取且为帧内预测模式,就叫标志位置为true。
//isAboveRightAvailable函数最后一个参数(neighborFlags + totalLeftUnits + 1 + numAboveUnits) 即为其在neighborFlags标志位开始的位置。
//isLeftAvailable函数最后一个参数(neighborFlags + totalLeftUnits - 1) 即为其在neighborFlags标志位结束的位置(在该函数中,标志位减减循环)。
//isBelowLeftAvailable函数最后一个参数 (neighborFlags + totalLeftUnits - 1 - numLeftUnits)即为其在neighborFlags标志位结束的位置(在该函数中,标志位减减循环)。
//numIntraNeighbor更新了全部可获取unit的数量。
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( predStride * predStride > m_iYuvExtSize, "Reference sample area not supported" );
const Pel* srcBuf = recoBuf.buf;//指向当前编码块内的左上第一个像素
const int srcStride = recoBuf.stride;
Pel* ptrDst = refBufUnfiltered;
const Pel* ptrSrc;
const Pel valueDC = 1 << (sps.getBitDepth( chType ) - 1);//若临近内容不可获取情况下的默认填充值。
//****************如果相邻内容全部不可获取
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; }
}
//****************如果相邻内容全部可获取
else if( numIntraNeighbor == totalUnits )
{
// 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]; }//左上角开始一直将上方全部循环完毕,循环predSize+1次
// 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; }//将左侧全部循环完成,循环predSize次
}
//****************相邻内容部分可获取
else // reference samples are partially available
{
//****************内容可获取的操作****************
// BB: old implementation using tmpLineBuf
// ---------------------------------------
Pel tmpLineBuf[5 * MAX_CU_SIZE];//像素级别的参考像素值缓存
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;//指向当前块外的左上角
ptrTmp = tmpLineBuf + (totalLeftUnits * unitHeight);//指向缓存中的左上角填充值
unitIdx = totalLeftUnits;//Idx为totalLeftUnits,此时代表左上角
if( neighborFlags[unitIdx] )//如果左上角unit可获取,对其unitWidth个参考样本赋值;若不可用,跳过
{
Pel topLeftVal = ptrSrc[0];
for( int j = 0; j < unitWidth; j++ ) { ptrTmp[j] = topLeftVal; }
}
// Fill left & below-left samples (downwards),填充左侧全部样本,从上往下
ptrSrc += srcStride;//从左上角移动到左侧,即左边列的第一个像素
ptrTmp--;//缓存指针前移
unitIdx--;//Idx前移
for( int k = 0; k < totalLeftUnits; k++ )
{
if( neighborFlags[unitIdx] )//判断当前Idx的参考unit是否可获取,可获取则循环unitHeight次进行赋值
{
for( int i = 0; i < unitHeight; i++ ) { ptrTmp[-i] = ptrSrc[i*srcStride]; }
}
ptrSrc += unitHeight*srcStride;//从上到下的进行
ptrTmp -= unitHeight;//缓存为了对应ptrSrc的读取,前移,反向进行
unitIdx--;//索引与缓存一致
}
// Fill above & above-right samples (left-to-right) (each unit has "unitWidth" samples),填充上方全部样本,从左至右
ptrSrc = srcBuf - srcStride;//指向水平方向参考的第一个,也是左上角的unit的右边第一个
ptrTmp = tmpLineBuf + (totalLeftUnits * unitHeight) + unitWidth; // offset line buffer by totalLeftUnits*unitHeight (for left/below-left) + unitWidth (for above-left)
//ptrTmp指向ptrSrc像素的临时缓存
unitIdx = totalLeftUnits + 1;//Idx算完左侧的+1,才是上方的。
for( int k = 0; k < totalAboveUnits; k++ )
{
if( neighborFlags[unitIdx] )//可获取
{
for( int j = 0; j < unitWidth; j++ ) { ptrTmp[j] = ptrSrc[j]; }//从默认值更新为可获取的值,通过ptrTmp写入tmpLineBuf中
}
ptrSrc += unitWidth;//一个unit完成,指向下一个
ptrTmp += unitWidth;
unitIdx++;//Idx正序,加加
}
//****************经过上述操作后,对内容为默认值部分采取的措施,填补为相邻可获取样本值****************
// Pad reference samples when necessary
int currUnit = 0;//从0开始
Pel* ptrTmpCurrUnit = tmpLineBuf;//指向存储每个像素点值的地址
if( !neighborFlags[0] )//Idx = 0表示左下列最下方的unit,不可获取,从其上面一个开始循环,所以nextUnit从1开始
{
int nextUnit = 1;
//找到第一个可以获取的点,nextUnit即为其Idx
while( nextUnit < totalUnits && !neighborFlags[nextUnit] )
{
nextUnit++;
}
//记录可获取的点的值(需要判断是左侧还是上方),nextUnit < totalLeftUnits是在左侧, = totalLeftUnits是左上角,否则在上方
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
//可获取的nextUnit可能是上方的,此时大于totalLeftUnits,currUnit取totalLeftUnits,说明左侧全部不可获取,全部赋值为refSample;
//可获取的nextUnit若在左侧,即小于totalLeftUnits,小于totalLeftUnits的单元即为不可获取的内容,赋值为refSample
while( currUnit < std::min<int>( nextUnit, totalLeftUnits ) )
{
//当前单元中每个像素均赋值为可获取的unit的第一个像素的值
for( int i = 0; i < unitHeight; i++ ) { ptrTmpCurrUnit[i] = refSample; }
ptrTmpCurrUnit += unitHeight;
currUnit++;
}
// fill top row
//currUnit在上面的循环中已经= totalLeftUnits,若
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);//不可获取的unit前一个可获取的单元,numSamplesInCurrUnit 个像素中最后一个像素的值。因为ptrTmpCurrUnit已经指向了当前不可获取的像素。
for( int k = 0; k < numSamplesInCurrUnit; k++ ) { ptrTmpCurrUnit[k] = refSample; }
}
ptrTmpCurrUnit += numSamplesInCurrUnit;
currUnit++;
}
// Copy processed samples,输出tmpLineBuf 中缓存的参考像素值
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]; }
}
}