对重建像素进行补偿
前面我们已经选取了最优的模式和补偿值,接下来需要根据最优的模式和补偿值对重新像素进行补偿(即对重建像素进行修正)。
入口函数
和选取最优模式一样,入口函数还是decideBlkParams
步骤如下:
1、对图像的每一个CTU进行处理
2、CTU尝试每一种SAO模式,选出最优的SAO模式
3、利用选取出来的SAO模式对重建块进行补偿
/*
** 选取最优模式
** 对图像的每一个CTU进行处理,对CTU尝试每一种SAO模式,选出最优的SAO模式
** 用选出来的最优SAO模式,对重建块进行处理
*/
Void TEncSampleAdaptiveOffset::decideBlkParams(TComPic* pic, Bool* sliceEnabled, SAOStatData*** blkStats, TComPicYuv* srcYuv, TComPicYuv* resYuv, SAOBlkParam* reconParams, SAOBlkParam* codedParams)
{
Bool isAllBlksDisabled = false;
if(!sliceEnabled[SAO_Y] && !sliceEnabled[SAO_Cb] && !sliceEnabled[SAO_Cr])
{
isAllBlksDisabled = true;
}
m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[ SAO_CABACSTATE_PIC_INIT ]);
SAOBlkParam modeParam;
Double minCost, modeCost;
// 遍历图像的所有CTU,对每一个CTU进行SAO处理
for(Int ctu=0; ctu< m_numCTUsPic; ctu++)
{
if(isAllBlksDisabled)
{
codedParams[ctu].reset();
continue;
}
m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[ SAO_CABACSTATE_BLK_CUR ]);
//get merge list
std::vector<SAOBlkParam*> mergeList;
// 获取merge列表
getMergeList(pic, ctu, reconParams, mergeList);
minCost = MAX_DOUBLE;
// 遍历所有的SAO模式
for(Int mode=0; mode < NUM_SAO_MODES; mode++)
{
switch(mode)
{
case SAO_MODE_OFF:
{
continue; //not necessary, since all-off case will be tested in SAO_MODE_NEW case.
}
break;
case SAO_MODE_NEW: // EO或者BO
{
deriveModeNewRDO(ctu, mergeList, sliceEnabled, blkStats, modeParam, modeCost, m_pppcRDSbacCoder, SAO_CABACSTATE_BLK_CUR);
}
break;
case SAO_MODE_MERGE: // merge模式
{
deriveModeMergeRDO(ctu, mergeList, sliceEnabled, blkStats , modeParam, modeCost, m_pppcRDSbacCoder, SAO_CABACSTATE_BLK_CUR);
}
break;
default:
{
printf("Not a supported SAO mode\n");
assert(0);
exit(-1);
}
}
// 选取最优的模式
if(modeCost < minCost)
{
minCost = modeCost;
codedParams[ctu] = modeParam;
m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[ SAO_CABACSTATE_BLK_NEXT ]);
}
} //mode
m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[ SAO_CABACSTATE_BLK_NEXT ]);
//apply reconstructed offsets
reconParams[ctu] = codedParams[ctu];
// 对重建像素块进行像素值补偿
reconstructBlkSAOParam(reconParams[ctu], mergeList);
offsetCTU(ctu, srcYuv, resYuv, reconParams[ctu], pic);
} //ctu
#if SAO_ENCODING_CHOICE
Int picTempLayer = pic->getSlice(0)->getDepth();
Int numLcusForSAOOff[NUM_SAO_COMPONENTS];
numLcusForSAOOff[SAO_Y ] = numLcusForSAOOff[SAO_Cb]= numLcusForSAOOff[SAO_Cr]= 0;
for (Int compIdx=0; compIdx<NUM_SAO_COMPONENTS; compIdx++)
{
for(Int ctu=0; ctu< m_numCTUsPic; ctu++)
{
if( reconParams[ctu][compIdx].modeIdc == SAO_MODE_OFF)
{
numLcusForSAOOff[compIdx]++;
}
}
}
#if SAO_ENCODING_CHOICE_CHROMA
for (Int compIdx=0; compIdx<NUM_SAO_COMPONENTS; compIdx++)
{
m_saoDisabledRate[compIdx][picTempLayer] = (Double)numLcusForSAOOff[compIdx]/(Double)m_numCTUsPic;
}
#else
if (picTempLayer == 0)
{
m_saoDisabledRate[SAO_Y][0] = (Double)(numLcusForSAOOff[SAO_Y]+numLcusForSAOOff[SAO_Cb]+numLcusForSAOOff[SAO_Cr])/(Double)(m_numCTUsPic*3);
}
#endif
#endif
}
对SAO参数进行调整
主要是对补偿值进行调整,对于比特深度为8的图像来说,这个一步没什么用
/*
** 对SAO参数进行一些调整
*/
Void TComSampleAdaptiveOffset::reconstructBlkSAOParam(SAOBlkParam& recParam, std::vector<SAOBlkParam*>& mergeList)
{
for(Int compIdx=0; compIdx< NUM_SAO_COMPONENTS; compIdx++)
{
SAOOffset& offsetParam = recParam[compIdx];
if(offsetParam.modeIdc == SAO_MODE_OFF)
{
continue;
}
switch(offsetParam.modeIdc)
{
case SAO_MODE_NEW: // 如果是EO或者BO模式,就对SAO参数(主要是补偿值)进行一定程度的调整
{
invertQuantOffsets(compIdx, offsetParam.typeIdc, offsetParam.typeAuxInfo, offsetParam.offset, offsetParam.offset);
}
break;
case SAO_MODE_MERGE: // 如果是merge模式,那么就获取附近CTU的SAO参数
{
SAOBlkParam* mergeTarget = mergeList[offsetParam.typeIdc];
assert(mergeTarget != NULL);
offsetParam = (*mergeTarget)[compIdx];
}
break;
default:
{
printf("Not a supported mode");
assert(0);
exit(-1);
}
}
}
}
对补偿值进行调整
对于比特深度为8的图像来说,这个一步没什么用
/*
** 对补偿值进行一定的调整
** 实际对于比特深度为8的图像来说,这个函数没什么用
*/
Void TComSampleAdaptiveOffset::invertQuantOffsets(Int compIdx, Int typeIdc, Int typeAuxInfo, Int* dstOffsets, Int* srcOffsets)
{
Int codedOffset[MAX_NUM_SAO_CLASSES];
::memcpy(codedOffset, srcOffsets, sizeof(Int)*MAX_NUM_SAO_CLASSES);
::memset(dstOffsets, 0, sizeof(Int)*MAX_NUM_SAO_CLASSES);
// BO模式
if(typeIdc == SAO_TYPE_START_BO)
{
for(Int i=0; i< 4; i++)
{
//
dstOffsets[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES] = codedOffset[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES]*(1<<m_offsetStepLog2[compIdx]);
}
}
else //EO模式
{
for(Int i=0; i< NUM_SAO_EO_CLASSES; i++)
{
dstOffsets[i] = codedOffset[i] *(1<<m_offsetStepLog2[compIdx]);
}
assert(dstOffsets[SAO_CLASS_EO_PLAIN] == 0); //keep EO plain offset as zero
}
}
对CTU进行补偿
遍历一个CTU的三个颜色分量,调用offsetBlock函数,进行具体的补偿操作
/*
** 对CTU进行补偿
*/
Void TComSampleAdaptiveOffset::offsetCTU(Int ctu, TComPicYuv* srcYuv, TComPicYuv* resYuv, SAOBlkParam& saoblkParam, TComPic* pPic)
{
Bool isLeftAvail,isRightAvail,isAboveAvail,isBelowAvail,isAboveLeftAvail,isAboveRightAvail,isBelowLeftAvail,isBelowRightAvail;
if(
(saoblkParam[SAO_Y ].modeIdc == SAO_MODE_OFF) &&
(saoblkParam[SAO_Cb].modeIdc == SAO_MODE_OFF) &&
(saoblkParam[SAO_Cr].modeIdc == SAO_MODE_OFF)
)
{
return;
}
//block boundary availability
pPic->getPicSym()->deriveLoopFilterBoundaryAvailibility(ctu, isLeftAvail,isRightAvail,isAboveAvail,isBelowAvail,isAboveLeftAvail,isAboveRightAvail,isBelowLeftAvail,isBelowRightAvail);
Int yPos = (ctu / m_numCTUInWidth)*m_maxCUHeight;
Int xPos = (ctu % 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;
// 遍历CTU的三个颜色分量
for(Int compIdx= 0; compIdx < NUM_SAO_COMPONENTS; compIdx++)
{
SAOOffset& ctbOffset = saoblkParam[compIdx];
if(ctbOffset.modeIdc != SAO_MODE_OFF)
{
Bool isLuma = (compIdx == SAO_Y);
Int formatShift= isLuma?0:1;
Int blkWidth = (width >> formatShift);
Int blkHeight = (height >> formatShift);
Int blkYPos = (yPos >> formatShift);
Int blkXPos = (xPos >> formatShift);
Int srcStride = isLuma?srcYuv->getStride():srcYuv->getCStride();
Pel* srcBlk = getPicBuf(srcYuv, compIdx)+ (yPos >> formatShift)*srcStride+ (xPos >> formatShift);
Int resStride = isLuma?resYuv->getStride():resYuv->getCStride();
Pel* resBlk = getPicBuf(resYuv, compIdx)+ blkYPos*resStride+ blkXPos;
// 对一个颜色分量进行补偿
offsetBlock( compIdx, ctbOffset.typeIdc, ctbOffset.offset
, srcBlk, resBlk, srcStride, resStride, blkWidth, blkHeight
, isLeftAvail, isRightAvail
, isAboveAvail, isBelowAvail
, isAboveLeftAvail, isAboveRightAvail
, isBelowLeftAvail, isBelowRightAvail
);
}
} //compIdx
}
对一个颜色分量进行补偿
补偿的过程就是修正重建块的像素值
/*
** 对重建像素块的一个颜色分量进行补偿
*/
Void TComSampleAdaptiveOffset::offsetBlock(Int compIdx, Int typeIdx, Int* offset
, Pel* srcBlk, Pel* resBlk, Int srcStride, Int resStride, Int width, Int height
, Bool isLeftAvail, Bool isRightAvail, Bool isAboveAvail, Bool isBelowAvail, Bool isAboveLeftAvail, Bool isAboveRightAvail, Bool isBelowLeftAvail, Bool isBelowRightAvail)
{
if(m_lineBufWidth != m_maxCUWidth)
{
m_lineBufWidth = m_maxCUWidth;
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* offsetClip = m_offsetClip[compIdx];
Int x,y, startX, startY, endX, endY, edgeType;
Int firstLineStartX, firstLineEndX, lastLineStartX, lastLineEndX;
Char signLeft, signRight, signDown;
Pel* srcLine = srcBlk;
Pel* resLine = resBlk;
switch(typeIdx)
{
case SAO_TYPE_EO_0: // EO_0模式
{
offset += 2;
startX = isLeftAvail ? 0 : 1;
endX = isRightAvail ? width : (width -1);
for (y=0; y< height; y++)
{
#if SAO_SGN_FUNC
signLeft = (Char)sgn(srcLine[startX] - srcLine[startX-1]);
#else
signLeft = (Char)m_sign[srcLine[startX] - srcLine[startX-1]];
#endif
for (x=startX; x< endX; x++)
{
#if SAO_SGN_FUNC
signRight = (Char)sgn(srcLine[x] - srcLine[x+1]);
#else
signRight = (Char)m_sign[srcLine[x] - srcLine[x+1]];
#endif
edgeType = signRight + signLeft;
signLeft = -signRight;
// 对重建块进行补偿
resLine[x] = offsetClip[srcLine[x] + offset[edgeType]];
}
srcLine += srcStride;
resLine += resStride;
}
}
break;
case SAO_TYPE_EO_90:
// 代码省略...
break;
case SAO_TYPE_EO_135:
// 代码省略...
break;
case SAO_TYPE_EO_45:
// 代码省略...
break;
case SAO_TYPE_BO:
// 代码省略...
break;
default:
{
printf("Not a supported SAO types\n");
assert(0);
exit(-1);
}
}
}