在一个compressSlice()中,在compressCU函数中实现对一个CU的编码,其中主要进行了CU的初始化,以及实际的编码操作。
- Void TEncCu::compressCU( TComDataCU*& rpcCU )
- {
- // initialize CU data
- m_ppcBestCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );
- m_ppcTempCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );
- #if RATE_CONTROL_LAMBDA_DOMAIN
- m_addSADDepth = 0;
- m_LCUPredictionSAD = 0;
- m_temporalSAD = 0;
- #endif
- // analysis of CU
- xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 );
- #if ADAPTIVE_QP_SELECTION
- if( m_pcEncCfg->getUseAdaptQpSelect() )
- {
- if(rpcCU->getSlice()->getSliceType()!=I_SLICE) //IIII
- {
- xLcuCollectARLStats( rpcCU);
- }
- }
- #endif
- }
xCompressCU函数由于包含了Intra和InterFrame编码的代码,因此同样非常长,共有600余行。下面着重对帧内编码的部分做一下梳理。
实现帧内编码的部分代码如下:
- Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, UInt uiDepth, PartSize eParentPartSize )
- {
- //......
- // do normal intra modes
- if ( !bEarlySkip )
- {
- // speedup for inter frames
- if( rpcBestCU->getSlice()->getSliceType() == I_SLICE ||
- rpcBestCU->getCbf( 0, TEXT_LUMA ) != 0 ||
- rpcBestCU->getCbf( 0, TEXT_CHROMA_U ) != 0 ||
- rpcBestCU->getCbf( 0, TEXT_CHROMA_V ) != 0 ) // avoid very complex intra if it is unlikely
- {
- xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_2Nx2N );
- rpcTempCU->initEstData( uiDepth, iQP );
- if( uiDepth == g_uiMaxCUDepth - g_uiAddCUDepth )
- {
- if( rpcTempCU->getWidth(0) > ( 1 << rpcTempCU->getSlice()->getSPS()->getQuadtreeTULog2MinSize() ) )
- {
- xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_NxN );
- rpcTempCU->initEstData( uiDepth, iQP );
- }
- }
- }
- }
- //......
- }
- Void TEncCu::xCheckRDCostIntra( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, PartSize eSize )
- {
- UInt uiDepth = rpcTempCU->getDepth( 0 );
- rpcTempCU->setSkipFlagSubParts( false, 0, uiDepth );
- rpcTempCU->setPartSizeSubParts( eSize, 0, uiDepth );
- rpcTempCU->setPredModeSubParts( MODE_INTRA, 0, uiDepth );
- rpcTempCU->setCUTransquantBypassSubParts( m_pcEncCfg->getCUTransquantBypassFlagValue(), 0, uiDepth );
- Bool bSeparateLumaChroma = true; // choose estimation mode
- UInt uiPreCalcDistC = 0;
- if( !bSeparateLumaChroma )
- {
- m_pcPredSearch->preestChromaPredMode( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth] );
- }
- m_pcPredSearch ->estIntraPredQT ( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth], m_ppcResiYuvTemp[uiDepth], m_ppcRecoYuvTemp[uiDepth], uiPreCalcDistC, bSeparateLumaChroma );
- m_ppcRecoYuvTemp[uiDepth]->copyToPicLuma(rpcTempCU->getPic()->getPicYuvRec(), rpcTempCU->getAddr(), rpcTempCU->getZorderIdxInCU() );
- m_pcPredSearch ->estIntraPredChromaQT( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth], m_ppcResiYuvTemp[uiDepth], m_ppcRecoYuvTemp[uiDepth], uiPreCalcDistC );
- m_pcEntropyCoder->resetBits();
- if ( rpcTempCU->getSlice()->getPPS()->getTransquantBypassEnableFlag())
- {
- m_pcEntropyCoder->encodeCUTransquantBypassFlag( rpcTempCU, 0, true );
- }
- m_pcEntropyCoder->encodeSkipFlag ( rpcTempCU, 0, true );
- m_pcEntropyCoder->encodePredMode( rpcTempCU, 0, true );
- m_pcEntropyCoder->encodePartSize( rpcTempCU, 0, uiDepth, true );
- m_pcEntropyCoder->encodePredInfo( rpcTempCU, 0, true );
- m_pcEntropyCoder->encodeIPCMInfo(rpcTempCU, 0, true );
- // Encode Coefficients
- Bool bCodeDQP = getdQPFlag();
- m_pcEntropyCoder->encodeCoeff( rpcTempCU, 0, uiDepth, rpcTempCU->getWidth (0), rpcTempCU->getHeight(0), bCodeDQP );
- setdQPFlag( bCodeDQP );
- if( m_bUseSBACRD ) m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]);
- rpcTempCU->getTotalBits() = m_pcEntropyCoder->getNumberOfWrittenBits();
- if(m_pcEncCfg->getUseSBACRD())
- {
- rpcTempCU->getTotalBins() = ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
- }
- rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );
- xCheckDQP( rpcTempCU );
- xCheckBestMode(rpcBestCU, rpcTempCU, uiDepth);
- }
在这个函数中,调用了estIntraPredQT和estIntraPredChromaQT方法,这两个函数的作用是类似的,区别只在于前者针对亮度分量后者针对色度分量。我们重点关注对亮度分量的操作,即estIntraPredQT函数。
下面是estIntraPredQT的一段代码:
- Void
- TEncSearch::estIntraPredQT( TComDataCU* pcCU,
- TComYuv* pcOrgYuv,
- TComYuv* pcPredYuv,
- TComYuv* pcResiYuv,
- TComYuv* pcRecoYuv,
- UInt& ruiDistC,
- Bool bLumaOnly )
- {
- //......
- for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ )
- {
- UInt uiMode = modeIdx;
- predIntraLumaAng( pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, bAboveAvail, bLeftAvail );
- // use hadamard transform here
- UInt uiSad = m_pcRdCost->calcHAD(g_bitDepthY, piOrg, uiStride, piPred, uiStride, uiWidth, uiHeight );
- UInt iModeBits = xModeBitsIntra( pcCU, uiMode, uiPU, uiPartOffset, uiDepth, uiInitTrDepth );
- Double cost = (Double)uiSad + (Double)iModeBits * m_pcRdCost->getSqrtLambda();
- CandNum += xUpdateCandList( uiMode, cost, numModesForFullRD, uiRdModeList, CandCostList );
- }
- //......
- }
这个for循环的意义就是遍历多种帧内预测模式,其中numModesAvailable==35,对应整个intra的35个模式。
在predIntraLumaAng函数中,编码器完成计算出当前PU的预测值:
- Void TComPrediction::predIntraLumaAng(TComPattern* pcTComPattern, UInt uiDirMode, Pel* piPred, UInt uiStride, Int iWidth, Int iHeight, Bool bAbove, Bool bLeft )
- {
- Pel *pDst = piPred;
- Int *ptrSrc;
- assert( g_aucConvertToBit[ iWidth ] >= 0 ); // 4x 4
- assert( g_aucConvertToBit[ iWidth ] <= 5 ); // 128x128
- assert( iWidth == iHeight );
- ptrSrc = pcTComPattern->getPredictorPtr( uiDirMode, g_aucConvertToBit[ iWidth ] + 2, m_piYuvExt );
- // get starting pixel in block
- Int sw = 2 * iWidth + 1;
- // Create the prediction
- if ( uiDirMode == PLANAR_IDX )
- {
- xPredIntraPlanar( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight );
- }
- else
- {
- if ( (iWidth > 16) || (iHeight > 16) )
- {
- xPredIntraAng(g_bitDepthY, ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, uiDirMode, bAbove, bLeft, false );
- }
- else
- {
- xPredIntraAng(g_bitDepthY, ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, uiDirMode, bAbove, bLeft, true );
- if( (uiDirMode == DC_IDX ) && bAbove && bLeft )
- {
- xDCPredFiltering( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight);
- }
- }
- }
- }
xPredIntraPlanar的作用是以平面模式构建当前PU的帧内预测块:
- Void TComPrediction::xPredIntraPlanar( Int* pSrc, Int srcStride, Pel* rpDst, Int dstStride, UInt width, UInt height )
- {
- assert(width == height);
- Int k, l, bottomLeft, topRight;
- Int horPred;
- Int leftColumn[MAX_CU_SIZE], topRow[MAX_CU_SIZE], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];
- UInt blkSize = width;
- UInt offset2D = width;
- UInt shift1D = g_aucConvertToBit[ width ] + 2;
- UInt shift2D = shift1D + 1;
- // Get left and above reference column and row
- for(k=0;k<blkSize+1;k++)
- {
- topRow[k] = pSrc[k-srcStride];
- leftColumn[k] = pSrc[k*srcStride-1];
- }
- // Prepare intermediate variables used in interpolation
- bottomLeft = leftColumn[blkSize];
- topRight = topRow[blkSize];
- for (k=0;k<blkSize;k++)
- {
- bottomRow[k] = bottomLeft - topRow[k];
- rightColumn[k] = topRight - leftColumn[k];
- topRow[k] <<= shift1D;
- leftColumn[k] <<= shift1D;
- }
- // Generate prediction signal
- for (k=0;k<blkSize;k++)
- {
- horPred = leftColumn[k] + offset2D;
- for (l=0;l<blkSize;l++)
- {
- horPred += rightColumn[k];
- topRow[l] += bottomRow[l];
- rpDst[k*dstStride+l] = ( (horPred + topRow[l]) >> shift2D );
- }
- }
- }
- Void TComPrediction::xPredIntraAng(Int bitDepth, Int* pSrc, Int srcStride, Pel*& rpDst, Int dstStride, UInt width, UInt height, UInt dirMode, Bool blkAboveAvailable, Bool blkLeftAvailable, Bool bFilter )
- {
- Int k,l;
- Int blkSize = width;
- Pel* pDst = rpDst;
- // Map the mode index to main prediction direction and angle
- assert( dirMode > 0 ); //no planar
- Bool modeDC = dirMode < 2;
- Bool modeHor = !modeDC && (dirMode < 18);
- Bool modeVer = !modeDC && !modeHor;
- Int intraPredAngle = modeVer ? (Int)dirMode - VER_IDX : modeHor ? -((Int)dirMode - HOR_IDX) : 0;
- Int absAng = abs(intraPredAngle);
- Int signAng = intraPredAngle < 0 ? -1 : 1;
- // Set bitshifts and scale the angle parameter to block size
- Int angTable[9] = {0, 2, 5, 9, 13, 17, 21, 26, 32};
- Int invAngTable[9] = {0, 4096, 1638, 910, 630, 482, 390, 315, 256}; // (256 * 32) / Angle
- Int invAngle = invAngTable[absAng];
- absAng = angTable[absAng];
- intraPredAngle = signAng * absAng;
- // Do the DC prediction
- if (modeDC)
- {
- Pel dcval = predIntraGetPredValDC(pSrc, srcStride, width, height, blkAboveAvailable, blkLeftAvailable);
- for (k=0;k<blkSize;k++)
- {
- for (l=0;l<blkSize;l++)
- {
- pDst[k*dstStride+l] = dcval;
- }
- }
- }
- // Do angular predictions
- else
- {
- Pel* refMain;
- Pel* refSide;
- Pel refAbove[2*MAX_CU_SIZE+1];
- Pel refLeft[2*MAX_CU_SIZE+1];
- // Initialise the Main and Left reference array.
- if (intraPredAngle < 0)
- {
- for (k=0;k<blkSize+1;k++)
- {
- refAbove[k+blkSize-1] = pSrc[k-srcStride-1];
- }
- for (k=0;k<blkSize+1;k++)
- {
- refLeft[k+blkSize-1] = pSrc[(k-1)*srcStride-1];
- }
- refMain = (modeVer ? refAbove : refLeft) + (blkSize-1);
- refSide = (modeVer ? refLeft : refAbove) + (blkSize-1);
- // Extend the Main reference to the left.
- Int invAngleSum = 128; // rounding for (shift by 8)
- for (k=-1; k>blkSize*intraPredAngle>>5; k--)
- {
- invAngleSum += invAngle;
- refMain[k] = refSide[invAngleSum>>8];
- }
- }
- else
- {
- for (k=0;k<2*blkSize+1;k++)
- {
- refAbove[k] = pSrc[k-srcStride-1];
- }
- for (k=0;k<2*blkSize+1;k++)
- {
- refLeft[k] = pSrc[(k-1)*srcStride-1];
- }
- refMain = modeVer ? refAbove : refLeft;
- refSide = modeVer ? refLeft : refAbove;
- }
- if (intraPredAngle == 0)
- {
- for (k=0;k<blkSize;k++)
- {
- for (l=0;l<blkSize;l++)
- {
- pDst[k*dstStride+l] = refMain[l+1];
- }
- }
- if ( bFilter )
- {
- for (k=0;k<blkSize;k++)
- {
- pDst[k*dstStride] = Clip3(0, (1<<bitDepth)-1, pDst[k*dstStride] + (( refSide[k+1] - refSide[0] ) >> 1) );
- }
- }
- }
- else
- {
- Int deltaPos=0;
- Int deltaInt;
- Int deltaFract;
- Int refMainIndex;
- for (k=0;k<blkSize;k++)
- {
- deltaPos += intraPredAngle;
- deltaInt = deltaPos >> 5;
- deltaFract = deltaPos & (32 - 1);
- if (deltaFract)
- {
- // Do linear filtering
- for (l=0;l<blkSize;l++)
- {
- refMainIndex = l+deltaInt+1;
- pDst[k*dstStride+l] = (Pel) ( ((32-deltaFract)*refMain[refMainIndex]+deltaFract*refMain[refMainIndex+1]+16) >> 5 );
- }
- }
- else
- {
- // Just copy the integer samples
- for (l=0;l<blkSize;l++)
- {
- pDst[k*dstStride+l] = refMain[l+deltaInt+1];
- }
- }
- }
- }
- // Flip the block if this is the horizontal mode
- if (modeHor)
- {
- Pel tmp;
- for (k=0;k<blkSize-1;k++)
- {
- for (l=k+1;l<blkSize;l++)
- {
- tmp = pDst[k*dstStride+l];
- pDst[k*dstStride+l] = pDst[l*dstStride+k];
- pDst[l*dstStride+k] = tmp;
- }
- }
- }
- }
- }