BMSVTM代码学习:解码器端的环路滤波处理

xiaoxiao2021-03-01  23

今天来看下环路滤波部分的代码。

环路滤波

为了便于理解,先来看HEVC编码框架。首先就要来说下混合编码框架,以下引自百度百科:

变换编码和预测编码是两类不同的压缩编码方法,如果将这两种方法组合在一起,就构成新的一类所谓混合编码,通常使用DCT等变换进行空间冗余度的压缩,用帧间预测或运动补偿预测进行时间冗余度的压缩,以达到对活动图像的更高的压缩效率。(百度百科)

HEVC框架如下,沿用了混合编码框架,整体可以分为几个模块:预测、变换量化、环路滤波、熵编码。可以看到预测、变换量化形成了一个环路,在反变换量化后进行了一步滤波处理这就是环路滤波。

这里所说的环路滤波器包含了去块效应滤波器和像素自适应补偿SAO滤波器,是环内滤波,属于环路后处理。还存在环外滤波,实际可以看做对输出视频再次进行滤波,不属于标准内容,具体技术一般由显示设备厂家自行选择。

在编码端环路滤波后的图像会被作为参考图像用于帧间预测,而在解码端就是最终要输出的图像了。

个人对于这部分内容并不熟悉,目前只需要了解整体代码框架,提取所需的去块前后和SAO前后图像,因此只对整体框架进行学习,具体理论后续补充。

DecLib::executeLoopFilters

DecApp::decode是一个很表层的函数了,其中调用的DecLib::decode是解码一帧图像的入口函数,解码后会调用DecLib::executeLoopFilters对解码得到的重构图像进行环路滤波。从这就可以清晰的理解所谓的环路后处理的含义了。这里立个flag吧,之后有时间好好整理下BMS/VTM解码器端的主要函数调用关系。

DecLib::executeLoopFilter只是一个上层入口函数,其中会调用LoopFilter::loopFilterPic和SampleAdaptiveOffset::SAOProcess进行具体的去块滤波和SAO。这里相比HM增加了ALF,不具体说了。

代码分析如下:

//环路滤波 Void DecLib::executeLoopFilters() { if( !m_pcPic ) { return; // nothing to deblock } CodingStructure& cs = *m_pcPic->cs; //取cs // deblocking filter m_cLoopFilter.loopFilterPic( cs ); //去块滤波 if( cs.sps->getUseSAO() ) { m_cSAO.SAOProcess( cs, cs.picture->getSAO() ); //SAO } #if JEM_TOOLS if( cs.sps->getSpsNext().getALFEnabled() ) //JEM o Adaptive loop filter(ALF) { ALFParam* alfParams = &cs.picture->getALFParam(); const UInt tidxMAX = E0104_ALF_MAX_TEMPLAYERID - 1u; const UInt tidx = cs.slice->getTLayer(); CHECK( tidx > tidxMAX, "index out of range" ); if( cs.slice->getPendingRasInit() || cs.slice->isIDRorBLA() ) { m_cALF.refreshAlfTempPred(); } if( alfParams->temporalPredFlag ) { m_cALF.loadALFParam( alfParams, alfParams->prevIdx, tidx ); } m_cALF.ALFProcess( cs, alfParams ); if( alfParams->alf_flag && !alfParams->temporalPredFlag ) { m_cALF.storeALFParam( alfParams, cs.slice->isIntra(), tidx, tidxMAX ); } } #endif }

LoopFilter::loopFilterPic

LoopFilter::loopFilterPic是去块效应滤波的入口函数,其中会以CU为单位,调用LoopFilter::xDeblockCU进行去块效应滤波。

在实际操作中,会以CTU为单位,遍历其中的所有CU,对每个CU进行滤波。而滤波会分两个方向进行,先垂直后水平。

在BMS/VTM中,因为引入了帧内色度分量单独划分,这里增加了针对该情况单独进行处理。

/** - call deblocking function for every CU . \param pcPic picture class (Pic) pointer */ //对每一个CU进行去块滤波 void LoopFilter::loopFilterPic( CodingStructure& cs ) { const PreCalcValues& pcv = *cs.pcv; //pcv DTRACE_UPDATE( g_trace_ctx, ( std::make_pair( "poc", cs.slice->getPOC() ) ) ); #if ENABLE_TRACING for( int y = 0; y < pcv.heightInCtus; y++ ) { for( int x = 0; x < pcv.widthInCtus; x++ ) { const UnitArea ctuArea( pcv.chrFormat, Area( x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth ) ); DTRACE ( g_trace_ctx, D_CRC, "CTU %d %d", ctuArea.Y().x, ctuArea.Y().y ); DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.picture->getRecoBuf( clipArea( ctuArea, *cs.picture ) ), &ctuArea.Y() ); } } #endif //以CTU为单位,垂直方向去块滤波 for( int y = 0; y < pcv.heightInCtus; y++ ) { for( int x = 0; x < pcv.widthInCtus; x++ ) { memset( m_aapucBS [EDGE_VER].data(), 0, m_aapucBS [EDGE_VER].byte_size() ); memset( m_aapbEdgeFilter[EDGE_VER].data(), false, m_aapbEdgeFilter[EDGE_VER].byte_size() ); const UnitArea ctuArea( pcv.chrFormat, Area( x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth ) ); // CU-based deblocking //遍历一个CTU中的所有CU for( auto &currCU : cs.traverseCUs( CS::getArea( cs, ctuArea, CH_L ), CH_L ) ) { xDeblockCU( currCU, EDGE_VER ); //垂直去块滤波 } if( CS::isDualITree( cs ) ) //如果色度单独划分 { memset( m_aapucBS [EDGE_VER].data(), 0, m_aapucBS [EDGE_VER].byte_size() ); memset( m_aapbEdgeFilter[EDGE_VER].data(), false, m_aapbEdgeFilter[EDGE_VER].byte_size() ); //遍历色度CU for( auto &currCU : cs.traverseCUs( CS::getArea( cs, ctuArea, CH_C ), CH_C ) ) { xDeblockCU( currCU, EDGE_VER ); } } } } // Vertical filtering //以CTU为单位,水平方向去块滤波 for( int y = 0; y < pcv.heightInCtus; y++ ) { for( int x = 0; x < pcv.widthInCtus; x++ ) { memset( m_aapucBS [EDGE_HOR].data(), 0, m_aapucBS [EDGE_HOR].byte_size() ); memset( m_aapbEdgeFilter[EDGE_HOR].data(), false, m_aapbEdgeFilter[EDGE_HOR].byte_size() ); const UnitArea ctuArea( pcv.chrFormat, Area( x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth ) ); // CU-based deblocking //遍历一个CTU中的所有CU for( auto &currCU : cs.traverseCUs( CS::getArea( cs, ctuArea, CH_L ), CH_L ) ) { xDeblockCU( currCU, EDGE_HOR ); //水平去块滤波 } if( CS::isDualITree( cs ) ) //如果色度单独划分 { memset( m_aapucBS [EDGE_HOR].data(), 0, m_aapucBS [EDGE_HOR].byte_size() ); memset( m_aapbEdgeFilter[EDGE_HOR].data(), false, m_aapbEdgeFilter[EDGE_HOR].byte_size() ); //遍历色度CU for( auto &currCU : cs.traverseCUs( CS::getArea( cs, ctuArea, CH_C ), CH_C ) ) { xDeblockCU( currCU, EDGE_HOR ); //水平去块滤波 } } } } DTRACE_PIC_COMP(D_REC_CB_LUMA_LF, cs, cs.getRecoBuf(), COMPONENT_Y); DTRACE_PIC_COMP(D_REC_CB_CHROMA_LF, cs, cs.getRecoBuf(), COMPONENT_Cb); DTRACE_PIC_COMP(D_REC_CB_CHROMA_LF, cs, cs.getRecoBuf(), COMPONENT_Cr); DTRACE ( g_trace_ctx, D_CRC, "LoopFilter" ); DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.getRecoBuf() ); }

SampleAdaptiveOffset::SAOProcess

SampleAdaptiveOffset::SAOProcess是SAO的入口函数,其中首先调用SampleAdaptiveOffset::xReconstructBlkSAOParams重构SAO块的参数,会遍历所有CTU调用SampleAdaptiveOffset::offsetCTU进行SAO。

//SAO Void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs, SAOBlkParam* saoBlkParams ) { CHECK(!saoBlkParams, "No parameters present"); xReconstructBlkSAOParams(cs, saoBlkParams); //重构块SAO参数 const UInt numberOfComponents = getNumberValidComponents(cs.area.chromaFormat); //可用通道数 Bool bAllDisabled = true; for (UInt compIdx = 0; compIdx < numberOfComponents; compIdx++) //分通道处理 { if (m_picSAOEnabled[compIdx]) { bAllDisabled = false; } } if (bAllDisabled) { return; } const PreCalcValues& pcv = *cs.pcv; PelUnitBuf rec = cs.getRecoBuf(); //重构图像 m_tempBuf.copyFrom( rec ); int ctuRsAddr = 0; //遍历每一个CTU,进行SAO for( UInt yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUHeight ) { for( UInt xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUWidth ) { const UInt width = (xPos + pcv.maxCUWidth > pcv.lumaWidth) ? (pcv.lumaWidth - xPos) : pcv.maxCUWidth; const UInt height = (yPos + pcv.maxCUHeight > pcv.lumaHeight) ? (pcv.lumaHeight - yPos) : pcv.maxCUHeight; const UnitArea area( cs.area.chromaFormat, Area(xPos , yPos, width, height) ); offsetCTU( area, m_tempBuf, rec, cs.picture->getSAO()[ctuRsAddr], cs); ctuRsAddr++; } } DTRACE_UPDATE(g_trace_ctx, (std::make_pair("poc", cs.slice->getPOC()))); DTRACE_PIC_COMP(D_REC_CB_LUMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Y); DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cb); DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cr); DTRACE ( g_trace_ctx, D_CRC, "SAO" ); DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.getRecoBuf() ); xPCMLFDisableProcess(cs); }
转载请注明原文地址: https://www.6miu.com/read-4050099.html

最新回复(0)