HEVC中SAO--自适应样点补偿:
本文分三个部分, 1.Sample Adaptive Offset原理, 2.SAO处理流程分析, 3.SAO意义何在!
a) SAO原理:
SAO是在DB之后进行, 输入是重建帧和原始帧数据, 输出是SAO数据和SAO后的重建帧. 自适应样点补偿是一个自适应选择过程,在去块滤波后进行。
下面是整个HEVC的编码框图, 可以看到SAO是在整个帧编码完成后得到重建帧后进行的,属于Slice级别(帧级).
首先把Frame划分为若干LCU, 然后对每个LCU中每个像素进行SAO操作.将根据其LCU像素特征选择一种像素补偿方式,以减少源图像与重构图像之间的失真。自适应样点补偿方式分为带状补偿(Band Offset,BO)和边缘补偿(Edge Offset,EO)两大类。
带状补偿将像素值强度等级划分为若干个条带,每个条带内的像素拥有相同的补偿值。进行补偿时根据重构像素点所处的条带,选择相应的带状补偿值进行补偿。
边缘补偿主要用于对图像的轮廓进行补偿。它将当前像素点值与相邻的2个像素值进行对比,用于比较的2个相邻像素可以在下图中所示的4种模板中选择,从而得到该像素点的类型。解码端根据码流中标示的像素点的类型信息进行相应的补偿校正。
对每个模板还要确定属于那种类型,类型确定有下面表格来决定.
b) SAO处理流程分析:
SAO主函数代码结构如下:主要有3个函数完成所有操作.
Void TEncSampleAdaptiveOffset::SAOProcess()
{ …
rdoSaoUnitAll(pcSaoParam, dLambdaLuma, dLambdaChroma, depth);
…
if (pcSaoParam->bSaoFlag[0])
processSaoUnitAll(saoLcuParam[0], oneUnitFlag[0], 0);
if (pcSaoParam->bSaoFlag[1])
{
processSaoUnitAll(saoLcuParam[1], oneUnitFlag[1], 1);
processSaoUnitAll(saoLcuParam[2], oneUnitFlag[2], 2);
}
}
其中TEncSampleAdaptiveOffset::rdoSaoUnitAll函数完成对整个Frame的所有LCU的YUV进行reset stats和calcSaoStatsCu,以及saoComponentParamDist得到最佳SAO_TYPE选择.最后encodeSaoOffset.
Void TEncSampleAdaptiveOffset::rdoSaoUnitAll()
{ …
for (idxY = 0; idxY< frameHeightInCU; idxY++)
{
for (idxX = 0; idxX< frameWidthInCU; idxX++)
{ …
// reset stats Y, Cb, Cr
…
calcSaoStatsCu(addr, compIdx, compIdx);
saoComponentParamDist();
sao2ChromaParamDist();
…
for ( compIdx=0;compIdx<3;compIdx++)
encodeSaoOffset(&saoLcuParam[compIdx][addr], compIdx);
// Cost of Merge
}
}
}
下面是子函数的说明:
也就是TEncSampleAdaptiveOffset::calcSaoStatsCuOrg
主要是得到LCU中所有像素各种SAOType的所有信息和状态统计,
分析记录各种SAOType(SAO_EO_0,SAO_EO_1,SAO_EO_2,SAO_EO_3,SAO_BO)以及各种SAOTypeLen和YUV分量对应的m_iOffsetOrg和m_iCount.
iStats = m_iOffsetOrg[0.1.2][0.1.2.3.4]; //YUV and SA0_TYPE
iCount = m_iCount[0.1.2][ 0.1.2.3.4];
如果是BO,通过iClassIdx =m_lumaTableBo[pRec[x]];来确定属于那个条带,并记录iStats[iClassIdx] += (pOrg[x] - pRec[x]);iCount[iClassIdx]++;这里iClassIdx大小为0~32.
如果是EO, 通过iClassIdx =m_auiEoTable[uiEdgeType]来确定模板类型,并记录iStats[iClassIdx] += (pOrg[x] -pRec[x]);iCount[iClassIdx]++;
这里iClassIdx大小为0~4.
其中映射关系如下:就是查表映射.
const UInt TComSampleAdaptiveOffset::m_auiEoTable[9] =
{ 1, //0 2, //1 0, //2 3, //3 4, //4
0, //5 0, //6 0, //7 0};
随后函数TEncSampleAdaptiveOffset::saoComponentParamDist完成最佳SAOTYPE的选择. 得到最佳dCostPartBest和typeIdx等信息.
下面两个语句得到每个BO带或者EO种类的offset.
m_iOffset[compIdx][typeIdx][classIdx] = m_iOffsetOrg[compIdx][typeIdx][classIdx]/(m_iCount[compIdx][typeIdx][classIdx];
m_iOffset[compIdx][typeIdx][classIdx] = Clip3(-m_iOffsetTh+1, m_iOffsetTh-1, (Int)m_iOffset[compIdx][typeIdx][classIdx]);
m_iOffset[compIdx][typeIdx][classIdx] = estIterOffset();
最后通过比较得到最佳dCostPartBest和typeIdx.
if(m_dCost[yCbCr][typeIdx] < dCostPartBest)
{
dCostPartBest = m_dCost[yCbCr][typeIdx];
// saoLcuParam保存目前最好的.
copySaoUnit(saoLcuParam, &saoLcuParamRdo );
bestDist = estDist;
}
同样TEncSampleAdaptiveOffset::sao2ChromaParamDist完成色度的选择.
TEncSampleAdaptiveOffset::SAOProcess()中的函数processSaoUnitAll主要完成SAO解码的操作,也就是对重建帧进行SAOoffset叠加.
另外一个同名函数SAOProcess属于COM库,主要是解码使用,也就调用processSaoUnitAll为主,负责恢复SAO偏移量.
Void TComSampleAdaptiveOffset::SAOProcess(TComPic* pcPic, SAOParam* pcSaoParam)
{ …
if (pcSaoParam->bSaoFlag[0] || pcSaoParam->bSaoFlag[1])
{ if (pcSaoParam->bSaoFlag[0])
{
processSaoUnitAll(saoLcuParam[iY], oneUnitFlag[iY], iY);
}
if(pcSaoParam->bSaoFlag[1])
{
processSaoUnitAll(saoLcuParam[1], oneUnitFlag[1], 1);//Cb
processSaoUnitAll(saoLcuParam[2], oneUnitFlag[2], 2);//Cr
}
}
}
a) SAO--自适应样点补偿意义何在:
SAO意义:大量模拟测试和资料显示, SAO平均可以节约2%到6%的码率, 而编解码的复杂度只增加2%左右!SAO主要目的和操作原理减少源图像与重构图像之间的失真。如果只看这点,实际上每帧编码后的码率反而会增加,因为多了SAO的相关语法和语义以及补偿值的编码!其实不然,虽然当前帧的码率增加了几个字节或者几个bit, 但是这点增加码字使得源图像与重构图像的失真减少,使接下来的预测残差更小了,从而大大的降低码率了!
我在想是否可以增加一个无损编码的HEVC版本呢? 增加另外一些语法和语义,把源图像与重构图像之间的失真单独拿出来再次采用其它无损编码技术编码,如果在需要超清晰的图像时,就可以和这个无损编码的码流组合解码,重建无损图像.个人随想!大家也可以发表下观点啊!
(如需转载请注明出处!谢谢!)