在compressSlice和encodeSlice之间,还有两个主要的处理函数,一个是SAOProcess,一个是ALFProcess(ALF是JEM中新加的);也就是说,当前帧完成重构后要对重构图像进行样点自适应补偿和自适应环路滤波,最后才进行真正的熵编码。
样点自适应补偿的原理是由于量化导致高频信息的丢失从而在解码图像上产生振铃效应,为此,样点自适应补偿技术从像素域入手,对重构曲线中出现的波峰像素添加复制进行补偿,波谷添加正值进行补偿。
今天先看样点自适应补偿,下面是主要函数解析:
if (pcSlice->getSPS()->getUseSAO())//如果可以使用SAO,则执行SAO过程;
{
Bool sliceEnabled[MAX_NUM_COMPONENT];
TComBitCounter tempBitCounter;
tempBitCounter.resetBits();
m_pcEncTop->getRDGoOnSbacCoder()->setBitstream(&tempBitCounter);
#if VCEG_AZ07_BAC_ADAPT_WDOW
m_pcSAO->setEntropyCoder(m_pcEntropyCoder);
#endif
m_pcSAO->initRDOCabacCoder(m_pcEncTop->getRDGoOnSbacCoder(), pcSlice);
//下面是执行样点自适应补偿的主函数
m_pcSAO->SAOProcess(pcPic, sliceEnabled, pcPic->getSlice(0)->getLambdas(), m_pcCfg->getTestSAODisableAtPictureLevel(), m_pcCfg->getSaoEncodingRate(), m_pcCfg->getSaoEncodingRateChroma(), m_pcCfg->getSaoCtuBoundary());
m_pcSAO->PCMLFDisableProcess(pcPic);
m_pcEncTop->getRDGoOnSbacCoder()->setBitstream(NULL);
//assign SAO slice header,分配SAO slice的头信息
for(Int s=0; s< uiNumSliceSegments; s++)
{
pcPic->getSlice(s)->setSaoEnabledFlag(CHANNEL_TYPE_LUMA, sliceEnabled[COMPONENT_Y]);
assert(sliceEnabled[COMPONENT_Cb] == sliceEnabled[COMPONENT_Cr]);
pcPic->getSlice(s)->setSaoEnabledFlag(CHANNEL_TYPE_CHROMA, sliceEnabled[COMPONENT_Cb]);
}
}
Void TEncSampleAdaptiveOffset::SAOProcess(TComPic* pPic, Bool* sliceEnabled, const Double *lambdas, const Bool bTestSAODisableAtPictureLevel, const Double saoEncodingRate, const Double saoEncodingRateChroma, Bool isPreDBFSamplesUsed )
{
TComPicYuv* orgYuv= pPic->getPicYuvOrg();//原始YUV
TComPicYuv* resYuv= pPic->getPicYuvRec();//重建YUV;
memcpy(m_lambda, lambdas, sizeof(m_lambda));
TComPicYuv* srcYuv = m_tempPicYuv;
resYuv->copyToPic(srcYuv);//将recYuv复制给srcYuv;
srcYuv->setBorderExtension(false);//将边界扩展设置为false;
srcYuv->extendPicBorder();//扩展图像边界;
//collect statistics
getStatistics(m_statData, orgYuv, srcYuv, pPic);//统计数据特性,结果存放在m_statData里
if(isPreDBFSamplesUsed)//如果使用预先去方块滤波
{
addPreDBFStatistics(m_statData);//在上一步得到的统计信息的基础上加上预先去方块滤波的信息
}
//slice on/off
decidePicParams(sliceEnabled, pPic, saoEncodingRate, saoEncodingRateChroma);//确定sliceEnabled的true或者false
//block on/off
SAOBlkParam* reconParams = new SAOBlkParam[m_numCTUsPic]; //temporary parameter buffer for storing reconstructed SAO parameters
//真正进行SAO操作的函数
decideBlkParams(pPic, sliceEnabled, m_statData, srcYuv, resYuv, reconParams, pPic->getPicSym()->getSAOBlkParam(), bTestSAODisableAtPictureLevel, saoEncodingRate, saoEncodingRateChroma);
delete[] reconParams;
}
Void TEncSampleAdaptiveOffset::getStatistics(SAOStatData*** blkStats, TComPicYuv* orgYuv, TComPicYuv* srcYuv, TComPic* pPic, Bool isCalculatePreDeblockSamples)
{
Bool isLeftAvail,isRightAvail,isAboveAvail,isBelowAvail,isAboveLeftAvail,isAboveRightAvail,isBelowLeftAvail,isBelowRightAvail;
const Int numberOfComponents = getNumberValidComponents(m_chromaFormatIDC);
for(Int ctuRsAddr= 0; ctuRsAddr < m_numCTUsPic; ctuRsAddr++)//遍历每个CTU
{
#if JVET_C0024_QTBT
Int yPos = (ctuRsAddr / m_numCTUInWidth)*m_CTUSize;
Int xPos = (ctuRsAddr % m_numCTUInWidth)*m_CTUSize;
Int height = (yPos + m_CTUSize > m_picHeight)?(m_picHeight- yPos):m_CTUSize;
Int width = (xPos + m_CTUSize > m_picWidth )?(m_picWidth - xPos):m_CTUSize;
#else
Int yPos = (ctuRsAddr / m_numCTUInWidth)*m_maxCUHeight;
Int xPos = (ctuRsAddr % m_numCTUInWidth)*m_maxCUWidth;
Int height = (yPos + m_maxCUHeight > m_picHeight)?(m_picHeight- yPos):m_maxCUHeight;
Int width = (xPos + m_maxCUWidth > m_picWidth )?(m_picWidth - xPos):m_maxCUWidth;
#endif
//判断环路滤波是否允许跨越slice或者tile的边界,并以此判断当前8个CTU的邻居是否可用
pPic->getPicSym()->deriveLoopFilterBoundaryAvailibility(ctuRsAddr, isLeftAvail,isRightAvail,isAboveAvail,isBelowAvail,isAboveLeftAvail,isAboveRightAvail,isBelowLeftAvail,isBelowRightAvail);
//NOTE: The number of skipped lines during gathering CTU statistics depends on the slice boundary availabilities.
//For simplicity, here only picture boundaries are considered.
//注意:在收集CTU数据期间,跳过的lines的数量取决于slice边界的可用性,简单起见,只考虑图像边界;
#if JVET_C0024_QTBT
isRightAvail = (xPos + m_CTUSize < m_picWidth );
isBelowAvail = (yPos + m_CTUSize < m_picHeight);
#else
isRightAvail = (xPos + m_maxCUWidth < m_picWidth );
isBelowAvail = (yPos + m_maxCUHeight < m_picHeight);
#endif
isBelowRightAvail = (isRightAvail && isBelowAvail);
isBelowLeftAvail = ((xPos > 0) && (isBelowAvail));
isAboveRightAvail = ((yPos > 0) && (isRightAvail));
for(Int compIdx = 0; compIdx < numberOfComponents; compIdx++)//遍历三个颜色分量
{
const ComponentID component = ComponentID(compIdx);
const UInt componentScaleX = getComponentScaleX(component, pPic->getChromaFormat());
const UInt componentScaleY = getComponentScaleY(component, pPic->getChromaFormat());
Int srcStride = srcYuv->getStride(component);
Pel* srcBlk = srcYuv->getAddr(component) + ((yPos >> componentScaleY) * srcStride) + (xPos >> componentScaleX);
Int orgStride = orgYuv->getStride(component);
Pel* orgBlk = orgYuv->getAddr(component) + ((yPos >> componentScaleY) * orgStride) + (xPos >> componentScaleX);
//统计像素的特性,然后判断像素属于EO或者BO的哪一个种类
getBlkStats(component, pPic->getPicSym()->getSPS().getBitDepth(toChannelType(component)), blkStats[ctuRsAddr][component]
, srcBlk, orgBlk, srcStride, orgStride, (width >> componentScaleX), (height >> componentScaleY)
, isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail
, isCalculatePreDeblockSamples
);
}
}
}
Void TEncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const Int channelBitDepth, SAOStatData* statsDataTypes
, Pel* srcBlk, Pel* orgBlk, Int srcStride, Int orgStride, Int width, Int height
, Bool isLeftAvail, Bool isRightAvail, Bool isAboveAvail, Bool isBelowAvail, Bool isAboveLeftAvail, Bool isAboveRightAvail
, Bool isCalculatePreDeblockSamples
)
{
#if JVET_C0024_QTBT
if(m_lineBufWidth != m_CTUSize)
{
m_lineBufWidth = m_CTUSize;
#else
if(m_lineBufWidth != m_maxCUWidth)
{
m_lineBufWidth = m_maxCUWidth;
#endif
if (m_signLineBuf1)
{
delete[] m_signLineBuf1;
m_signLineBuf1 = NULL;
}
m_signLineBuf1 = new Char[m_lineBufWidth+1];
if (m_signLineBuf2)
{
delete[] m_signLineBuf2;
m_signLineBuf2 = NULL;
}
m_signLineBuf2 = new Char[m_lineBufWidth+1];
}
Int x,y, startX, startY, endX, endY, edgeType, firstLineStartX, firstLineEndX;
Char signLeft, signRight, signDown;
Int64 *diff, *count;
Pel *srcLine, *orgLine;
Int* skipLinesR = m_skipLinesR[compIdx];
Int* skipLinesB = m_skipLinesB[compIdx];
for(Int typeIdx=0; typeIdx< NUM_SAO_NEW_TYPES; typeIdx++)//遍历SAO的NEW的5种类型,分别为EO_0,EO_1,EO_2,EO_3和BO
{
SAOStatData& statsData= statsDataTypes[typeIdx];
statsData.reset();
srcLine = srcBlk;//重建块
orgLine = orgBlk;//原始块
diff = statsData.diff;
count = statsData.count;
switch(typeIdx)
{
case SAO_TYPE_EO_0://统计4个划分种类各自的差值之和和像素个数
{
diff +=2;
count+=2;
endY = (isBelowAvail) ? (height - skipLinesB[typeIdx]) : height;
startX = (!isCalculatePreDeblockSamples) ? (isLeftAvail ? 0 : 1)
: (isRightAvail ? (width - skipLinesR[typeIdx]) : (width - 1))
;
endX = (!isCalculatePreDeblockSamples) ? (isRightAvail ? (width - skipLinesR[typeIdx]) : (width -