1. 程式人生 > >VVC程式碼 BMS 幀內預測學習之四:xFillReferenceSamples()

VVC程式碼 BMS 幀內預測學習之四:xFillReferenceSamples()

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]; } } }