VVC程式碼 BMS 幀內預測學習之四:xFillReferenceSamples()
阿新 • • 發佈:2018-12-11
xFillReferenceSamples()函式是參考畫素的獲取過程。 主要步驟: 1、分析臨近的畫素是否可獲取 2、進行參考樣本的填充:若臨近的畫素全部可獲取,則賦值;全部不可獲取,則賦預設值;若部分可獲取,則對可獲取的賦對應的值,不可獲取的用預設值填充。 注:幀內預測過程中有一個比較混淆的地方,預測過程中會採取unitWidth及unitHeight的方式,將預測畫素分為unit進行操作的。(如一個4x32的塊,若unitWidth為4,unitHeight為4,則預測過程的Idx等都是按照橫向為4/4=1,縱向為32/4=8進行的,左上角的點單元採用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;
//幀內預測中,是以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的currUnit即為不可獲取的內容,賦值為refSample
while( currUnit < std::min<int>( nextUnit, totalLeftUnits ) )
{
//unit中每個畫素均賦值為可獲取的unit的第一個畫素的值
for( int i = 0; i < unitHeight; i++ ) { ptrTmpCurrUnit[i] = refSample; }
ptrTmpCurrUnit += unitHeight;
currUnit++;
}
// fill top row
//currUnit在上面的迴圈中已經= totalLeftUnits,若<nextUnit,說明可獲取的Idx在上方。
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前一個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]; }
}
}