1、HM10 encoder关于SAO的初始化操作
在编码开始之前,编码器将进行与SAO有关的一些初始化设置。如在TAppEncTop::encode()函数中调用TAppEncTop::xInitLibCfg()进行与config相关的设置:
m_cTEncTop.setLFCrossSliceBoundaryFlag( m_bLFCrossSliceBoundaryFlag );//parseCfg时设置,指定loop filter是否能跨越slice边界,默认为false m_cTEncTop.setUseSAO ( m_bUseSAO );//parseCfg时设置,指定SAO功能是否开启,由cfg文件指定 m_cTEncTop.setMaxNumOffsetsPerPic (m_maxNumOffsetsPerPic);//每一个像素最大允许的偏移量,默认为2048 m_cTEncTop.setSaoLcuBoundary (m_saoLcuBoundary);//是否使用非deblocked像素做SAO参数估计 m_cTEncTop.setSaoLcuBasedOptimization (m_saoLcuBasedOptimization);//选择基于picture还是CTU的估计,默认选择CTU
在TEncTop::create ()中,会依据前面配置的结果进行初始化操作:
if (m_bUseSAO) { m_cEncSAO.setSaoLcuBoundary(getSaoLcuBoundary()); m_cEncSAO.setSaoLcuBasedOptimization(getSaoLcuBasedOptimization()); m_cEncSAO.setMaxNumOffsetsPerPic(getMaxNumOffsetsPerPic()); m_cEncSAO.create( getSourceWidth(), getSourceHeight(), g_uiMaxCUWidth, g_uiMaxCUHeight ); m_cEncSAO.createEncBuffer(); }
Void TEncGOP::init ( TEncTop* pcTEncTop ) {//...... //--Adaptive Loop filter m_pcSAO = pcTEncTop->getSAO(); //...... }
2、实际进行SAO滤波操作
在TEncGOP::compressGOP函数中调用SAOProcess实现:
Void TEncSampleAdaptiveOffset::SAOProcess(SAOParam *pcSaoParam, Double dLambda) #endif { m_dLambdaLuma = dLambdaLuma; m_dLambdaChroma = dLambdaChroma; if(m_bUseNIF) { m_pcPic->getPicYuvRec()->copyToPic(m_pcYuvTmp); } m_uiSaoBitIncreaseY = max(g_bitDepthY - 10, 0); m_uiSaoBitIncreaseC = max(g_bitDepthC - 10, 0); m_iOffsetThY = 1 << min(g_bitDepthY - 5, 5); m_iOffsetThC = 1 << min(g_bitDepthC - 5, 5); resetSAOParam(pcSaoParam); if( !m_saoLcuBasedOptimization || !m_saoLcuBoundary ) { resetStats(); } Double dCostFinal = 0; if ( m_saoLcuBasedOptimization) { rdoSaoUnitAll(pcSaoParam, dLambdaLuma, dLambdaChroma, depth); } else { pcSaoParam->bSaoFlag[0] = 1; pcSaoParam->bSaoFlag[1] = 0; dCostFinal = 0; Double lambdaRdo = dLambdaLuma; resetStats(); getSaoStats(pcSaoParam->psSaoPart[0], 0); runQuadTreeDecision(pcSaoParam->psSaoPart[0], 0, dCostFinal, m_uiMaxSplitLevel, lambdaRdo, 0); pcSaoParam->bSaoFlag[0] = dCostFinal < 0 ? 1:0; if(pcSaoParam->bSaoFlag[0]) { convertQT2SaoUnit(pcSaoParam, 0, 0); assignSaoUnitSyntax(pcSaoParam->saoLcuParam[0], pcSaoParam->psSaoPart[0], pcSaoParam->oneUnitFlag[0], 0); } } if (pcSaoParam->bSaoFlag[0]) { processSaoUnitAll( pcSaoParam->saoLcuParam[0], pcSaoParam->oneUnitFlag[0], 0); } if (pcSaoParam->bSaoFlag[1]) { processSaoUnitAll( pcSaoParam->saoLcuParam[1], pcSaoParam->oneUnitFlag[1], 1); processSaoUnitAll( pcSaoParam->saoLcuParam[2], pcSaoParam->oneUnitFlag[2], 2); } }
具体的实现原理下篇继续研究