HEVC中SAO--自适应样点补偿 分析解读

2024-02-12 09:32

本文主要是介绍HEVC中SAO--自适应样点补偿 分析解读,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

HEVC SAO

目录(?)[+]

原文地址:http://blog.csdn.net/feixiang_john/article/details/8258452

HEVC中SAO--自适应样点补偿:本文分三个部分, 1.Sample Adaptive Offset原理, 2.SAO处理流程分析, 3.SAO意义何在!

SAO的文章见《Sample Adaptive Offset in the HEVC Standard》谷歌学术

a)  SAO原理

    SAO是在DB之后进行, 输入是重建帧和原始帧数据, 输出是SAO数据和SAO后的重建帧. 自适应样点补偿是一个自适应选择过程,在去块滤波后进行。下面是整个HEVC的编码框图, 可以看到SAO是在整个帧编码完成后得到重建帧后进行的,属于Slice级别(帧级).


首先把Frame划分为若干LCU, 然后对每个LCU中每个像素进行SAO操作.将根据其LCU像素特征选择一种像素补偿方式,以减少源图像与重构图像之间的失真。自适应样点补偿方式分为带状补偿(Band Offset,BO)和边缘补偿(Edge Offset,EO)两大类。

带状补偿将像素值强度等级划分为若干个条带,每个条带内的像素拥有相同的补偿值。进行补偿时根据重构像素点所处的条带,选择相应的带状补偿值进行补偿。

    边缘补偿主要用于对图像的轮廓进行补偿。它将当前像素点值与相邻的2个像素值进行对比,用于比较的2个相邻像素可以在下图中所示的4种模板中选择,从而得到该像素点的类型。解码端根据码流中标示的像素点的类型信息进行相应的补偿校正。


对每个模板还要确定属于那种类型,类型确定有下面表格来决定.

类别 
1c < a&&c< b
2(c< a && c==b) || (c==a&&c < b)
3(c> a && c==b) || (c==a&&c > b)
4c > a&&c> b
0None of the above


b)  SAO处理流程分析

    SAO主函数代码结构如下:主要有3个函数完成所有操作.

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. Void TEncSampleAdaptiveOffset::SAOProcess()  
  2. { …  
  3. rdoSaoUnitAll(pcSaoParam, dLambdaLuma, dLambdaChroma, depth);  
  4. …  
  5. if (pcSaoParam->bSaoFlag[0])  
  6.  processSaoUnitAll(saoLcuParam[0], oneUnitFlag[0], 0);  
  7. if (pcSaoParam->bSaoFlag[1])  
  8. {  
  9. processSaoUnitAll(saoLcuParam[1], oneUnitFlag[1], 1);  
  10. processSaoUnitAll(saoLcuParam[2], oneUnitFlag[2], 2);  
  11. }  
  12. }  

    其中TEncSampleAdaptiveOffset::rdoSaoUnitAll函数完成对整个Frame的所有LCU的YUV进行reset stats和calcSaoStatsCu,以及saoComponentParamDist得到最佳SAO_TYPE选择.最后encodeSaoOffset.

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. Void TEncSampleAdaptiveOffset::rdoSaoUnitAll()  
  2. { …  
  3.  for (idxY = 0; idxY< frameHeightInCU; idxY++)  
  4.  {  
  5.    for (idxX = 0; idxX< frameWidthInCU; idxX++)  
  6. { …  
  7. // reset stats Y, Cb, Cr  
  8. …  
  9.      calcSaoStatsCu(addr, compIdx,  compIdx);  
  10.      saoComponentParamDist();  
  11.      sao2ChromaParamDist();  
  12. …  
  13.      for ( compIdx=0;compIdx<3;compIdx++)  
  14.       encodeSaoOffset(&saoLcuParam[compIdx][addr], compIdx);  
  15.        // Cost of Merge  
  16.    }  
  17.  }  
  18. }  

    下面是子函数的说明:

也就是TEncSampleAdaptiveOffset::calcSaoStatsCuOrg

主要是得到LCU中所有像素各种SAOType的所有信息和状态统计,

分析记录各种SAOType(SAO_EO_0,SAO_EO_1,SAO_EO_2,SAO_EO_3,SAO_BO)以及各种SAOTypeLen和YUV分量对应的m_iOffsetOrgm_iCount.

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. iStats = m_iOffsetOrg[0.1.2][0.1.2.3.4]; //YUV and SA0_TYPE  
  2. 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.

其中映射关系如下:就是查表映射.

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. const UInt TComSampleAdaptiveOffset::m_auiEoTable[9] =  
  2. { 1, //0   
  3. 2, //1  
  4. 0, //2  
  5. 3, //3  
  6. 4, //4  
  7.  0, //5   
  8. 0, //6  
  9. 0, //7  
  10. 0};  


随后函数TEncSampleAdaptiveOffset::saoComponentParamDist完成最佳SAOTYPE的选择. 得到最佳dCostPartBest和typeIdx等信息.

下面两个语句得到每个BO带或者EO种类的offset.

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. m_iOffset[compIdx][typeIdx][classIdx] = m_iOffsetOrg[compIdx][typeIdx][classIdx]/(m_iCount[compIdx][typeIdx][classIdx];  
  2. m_iOffset[compIdx][typeIdx][classIdx] = Clip3(-m_iOffsetTh+1, m_iOffsetTh-1, (Int)m_iOffset[compIdx][typeIdx][classIdx]);  
  3. m_iOffset[compIdx][typeIdx][classIdx] = estIterOffset();  

最后通过比较得到最佳dCostPartBest 和typeIdx.

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1.    if(m_dCost[yCbCr][typeIdx] < dCostPartBest)  
  2.    {  
  3.      dCostPartBest = m_dCost[yCbCr][typeIdx];  
  4. // saoLcuParam保存目前最好的.  
  5.      copySaoUnit(saoLcuParam, &saoLcuParamRdo );       
  6. bestDist = estDist;        
  7. }  

同样TEncSampleAdaptiveOffset::sao2ChromaParamDist完成色度的选择.

TEncSampleAdaptiveOffset::SAOProcess()中的函数processSaoUnitAll主要完成SAO解码的操作,也就是对重建帧进行SAOoffset叠加.

另外一个同名函数SAOProcess属于COM库,主要是解码使用,也就调用processSaoUnitAll为主,负责恢复SAO偏移量.

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. Void TComSampleAdaptiveOffset::SAOProcess(TComPic* pcPic, SAOParam* pcSaoParam)  
  2. { …  
  3. if (pcSaoParam->bSaoFlag[0] || pcSaoParam->bSaoFlag[1])  
  4.  { if (pcSaoParam->bSaoFlag[0])  
  5.    {  
  6.      processSaoUnitAll(saoLcuParam[iY], oneUnitFlag[iY], iY);  
  7.    }  
  8.    if(pcSaoParam->bSaoFlag[1])  
  9.    {  
  10.       processSaoUnitAll(saoLcuParam[1], oneUnitFlag[1], 1);//Cb  
  11.       processSaoUnitAll(saoLcuParam[2], oneUnitFlag[2], 2);//Cr  
  12.    }  
  13.  }  
  14. }  

c)   SAO--自适应样点补偿意义


     SAO意义:大量模拟测试和资料显示, SAO平均可以节约2%到6%的码率, 而编解码的复杂度只增加2%左右!SAO主要目的和操作原理减少源图像与重构图像之间的失真。如果只看这点,实际上每帧编码后的码率反而会增加,因为多了SAO的相关语法和语义以及补偿值的编码!其实不然,虽然当前帧的码率增加了几个字节或者几个bit, 但是这点增加码字使得源图像与重构图像的失真减少,使接下来的预测残差更小了,从而大大的降低码率了!

     我在想是否可以增加一个无损编码的HEVC版本呢? 增加另外一些语法和语义,把源图像与重构图像之间的失真单独拿出来再次采用其它无损编码技术编码,如果在需要超清晰的图像时,就可以和这个无损编码的码流组合解码,重建无损图像.个人随想!大家也可以发表下观点啊!


代码注释:

(一)http://blog.csdn.net/hevc_cjl/article/details/8288479

关于SAO的原理和流程的解析,已经在我转载的一篇博客HEVC中SAO--自适应样点补偿 详细分析解读有了比较清楚的介绍了,本文就不再重复这个过程,而把主要精力放在具体函数实现的解析上。在我自己的一篇博客HEVC学习(八) —— 以SAO为例浅析跟踪代码方法里其实也作了相关的铺垫了,当中重点放在跟踪代码的方法上,本文在此基础上对重要的函数进行解析,它们的调用位置这里就不提了,有关这部分的内容请参考前述的两篇博客。

本文首先介绍

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. m_cEncSAO.create( getSourceWidth(), getSourceHeight(), g_uiMaxCUWidth, g_uiMaxCUHeight, _uiMaxCUDepth );  

该函数为SAO的相关参数分配内存和初始化。

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** create SampleAdaptiveOffset memory. 
  2.  * \param  
  3.  */  
  4. Void TComSampleAdaptiveOffset::create( UInt uiSourceWidth, UInt uiSourceHeight, UInt uiMaxCUWidth, UInt uiMaxCUHeight, UInt uiMaxCUDepth)  
  5. {  
  6.   m_iPicWidth  = uiSourceWidth;  
  7.   m_iPicHeight = uiSourceHeight;  
  8.   
  9.   m_uiMaxCUWidth  = uiMaxCUWidth;  
  10.   m_uiMaxCUHeight = uiMaxCUHeight;  
  11.   
  12.   m_iNumCuInWidth  = m_iPicWidth / m_uiMaxCUWidth;  
  13.   m_iNumCuInWidth += ( m_iPicWidth % m_uiMaxCUWidth ) ? 1 : 0;  
  14.   
  15.   m_iNumCuInHeight  = m_iPicHeight / m_uiMaxCUHeight;  
  16.   m_iNumCuInHeight += ( m_iPicHeight % m_uiMaxCUHeight ) ? 1 : 0;  
  17.   //! 以下四句根据CU的实际尺寸计算可划分的最大深度  
  18.   Int iMaxSplitLevelHeight = (Int)(logf((Float)m_iNumCuInHeight)/logf(2.0));  
  19.   Int iMaxSplitLevelWidth  = (Int)(logf((Float)m_iNumCuInWidth )/logf(2.0));  
  20.   
  21.   m_uiMaxSplitLevel = (iMaxSplitLevelHeight < iMaxSplitLevelWidth)?(iMaxSplitLevelHeight):(iMaxSplitLevelWidth);  
  22.   m_uiMaxSplitLevel = (m_uiMaxSplitLevel< m_uiMaxDepth)?(m_uiMaxSplitLevel):(m_uiMaxDepth);  
  23.   /* various structures are overloaded to store per component data. 
  24.    * m_iNumTotalParts must allow for sufficient storage in any allocated arrays */  
  25.   /* 
  26.   const Int TComSampleAdaptiveOffset::m_aiNumCulPartsLevel[5] = 
  27.   { 
  28.   1,   //level 0 
  29.   5,   //level 1 
  30.   21,  //level 2 
  31.   85,  //level 3 
  32.   341, //level 4 
  33.   }; 
  34.   */  
  35.   m_iNumTotalParts  = max(3,m_aiNumCulPartsLevel[m_uiMaxSplitLevel]);  
  36.   
  37.   UInt uiPixelRangeY = 1 << g_bitDepthY; //!< Y分量的像素范围(256 for 8it-depth)  
  38.   UInt uiBoRangeShiftY = g_bitDepthY - SAO_BO_BITS; //!< Band Offset (BO)中每个band的宽度(3 for 8bit-depth,即1<<3 = 8)(Y分量)  
  39.   
  40.   m_lumaTableBo = new Pel [uiPixelRangeY]; //!< Y分量在BO模式下的索引表,根据像素值可以索引到对应的band序号  
  41.   for (Int k2=0; k2<uiPixelRangeY; k2++)  
  42.   {  
  43.     m_lumaTableBo[k2] = 1 + (k2>>uiBoRangeShiftY); //!< 总共分了32个band(1~32)  
  44.   }  
  45.   
  46.   UInt uiPixelRangeC = 1 << g_bitDepthC; //!< CbCr分量的像素范围(256 for 8it-depth)  
  47.   UInt uiBoRangeShiftC = g_bitDepthC - SAO_BO_BITS; //!< Band Offset (BO)中每个band的宽度(3 for 8bit-depth,即1<<3 = 8)(CbCr分量)  
  48.   
  49.   m_chromaTableBo = new Pel [uiPixelRangeC]; //!< CbCr分量在BO模式下的索引表,根据像素值可以索引到对应的band序号  
  50.   for (Int k2=0; k2<uiPixelRangeC; k2++)  
  51.   {  
  52.     m_chromaTableBo[k2] = 1 + (k2>>uiBoRangeShiftC); //!< 总共分了32个band(1~32)  
  53.   }  
  54.   
  55.   m_iUpBuff1 = new Int[m_iPicWidth+2];  
  56.   m_iUpBuff2 = new Int[m_iPicWidth+2];  
  57.   m_iUpBufft = new Int[m_iPicWidth+2];  
  58.   
  59.   m_iUpBuff1++;  
  60.   m_iUpBuff2++;  
  61.   m_iUpBufft++;  
  62.   Pel i;  
  63.   
  64.   UInt uiMaxY  = (1 << g_bitDepthY) - 1; //!< Y分量像素最大值(255 for 8bit-depth)  
  65.   UInt uiMinY  = 0;  
  66.   
  67.   Int iCRangeExt = uiMaxY>>1; //!< 对范围进行扩展  
  68.   
  69.   m_pClipTableBase = new Pel[uiMaxY+2*iCRangeExt];  
  70.   m_iOffsetBo      = new Int[uiMaxY+2*iCRangeExt];  
  71.   
  72.   for(i=0;i<(uiMinY+iCRangeExt);i++)  
  73.   {  
  74.     m_pClipTableBase[i] = uiMinY;  
  75.   }  
  76.   
  77.   for(i=uiMinY+iCRangeExt;i<(uiMaxY+  iCRangeExt);i++)  
  78.   {  
  79.     m_pClipTableBase[i] = i-iCRangeExt;  
  80.   }  
  81.   
  82.   for(i=uiMaxY+iCRangeExt;i<(uiMaxY+2*iCRangeExt);i++)  
  83.   {  
  84.     m_pClipTableBase[i] = uiMaxY;  
  85.   }  
  86.   //! 0 0 0 ... 0 1 2 3 4 ... 255 255 ... 255//!  
  87.   m_pClipTable = &(m_pClipTableBase[iCRangeExt]); //!< 查找表  
  88.   
  89.   UInt uiMaxC  = (1 << g_bitDepthC) - 1; //!< CbCr分量像素最大值(255 for 8bit-depth)  
  90.   UInt uiMinC  = 0;  
  91.   
  92.   Int iCRangeExtC = uiMaxC>>1; //!< 对范围进行扩展  
  93.   
  94.   m_pChromaClipTableBase = new Pel[uiMaxC+2*iCRangeExtC];  
  95.   m_iChromaOffsetBo      = new Int[uiMaxC+2*iCRangeExtC];  
  96.   
  97.   for(i=0;i<(uiMinC+iCRangeExtC);i++)  
  98.   {  
  99.     m_pChromaClipTableBase[i] = uiMinC;  
  100.   }  
  101.   
  102.   for(i=uiMinC+iCRangeExtC;i<(uiMaxC+  iCRangeExtC);i++)  
  103.   {  
  104.     m_pChromaClipTableBase[i] = i-iCRangeExtC;  
  105.   }  
  106.   
  107.   for(i=uiMaxC+iCRangeExtC;i<(uiMaxC+2*iCRangeExtC);i++)  
  108.   {  
  109.     m_pChromaClipTableBase[i] = uiMaxC;  
  110.   }  
  111.   //! 0 0 0 ... 0 ! 1 2 3 4 ... 255 255 ... 255 //!  
  112.   m_pChromaClipTable = &(m_pChromaClipTableBase[iCRangeExtC]); //!< 查找表,实际上与Y分量是相同的  
  113.   
  114.   m_iLcuPartIdx = new Int [m_iNumCuInHeight*m_iNumCuInWidth];  
  115.   m_pTmpL1 = new Pel [m_uiMaxCUHeight+1];  
  116.   m_pTmpL2 = new Pel [m_uiMaxCUHeight+1];  
  117.   m_pTmpU1 = new Pel [m_iPicWidth];  
  118.   m_pTmpU2 = new Pel [m_iPicWidth];  
  119. }</span>  

(二)http://blog.csdn.net/hevc_cjl/article/details/8288572

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** rate distortion optimization of all SAO units 
  2.  * \param saoParam SAO parameters 
  3.  * \param lambda  
  4.  * \param lambdaChroma 
  5.  */  
  6. #if SAO_ENCODING_CHOICE  
  7. Void TEncSampleAdaptiveOffset::rdoSaoUnitAll(SAOParam *saoParam, Double lambda, Double lambdaChroma, Int depth)  
  8. #else  
  9. Void TEncSampleAdaptiveOffset::rdoSaoUnitAll(SAOParam *saoParam, Double lambda, Double lambdaChroma)  
  10. #endif  
  11. {  
  12.   
  13.   Int idxY;  
  14.   Int idxX;  
  15.   Int frameHeightInCU = saoParam->numCuInHeight;  
  16.   Int frameWidthInCU  = saoParam->numCuInWidth;  
  17.   Int j, k;  
  18.   Int addr = 0;  
  19.   Int addrUp = -1;  
  20.   Int addrLeft = -1;  
  21.   Int compIdx = 0;  
  22.   SaoLcuParam mergeSaoParam[3][2];  
  23.   Double compDistortion[3];  
  24.   
  25.   saoParam->bSaoFlag[0] = true;  
  26.   saoParam->bSaoFlag[1] = true;  
  27.   saoParam->oneUnitFlag[0] = false;  
  28.   saoParam->oneUnitFlag[1] = false;  
  29.   saoParam->oneUnitFlag[2] = false;  
  30.   
  31. #if SAO_ENCODING_CHOICE  
  32. #if SAO_ENCODING_CHOICE_CHROMA  
  33.   Int numNoSao[2];  
  34.   numNoSao[0] = 0;// Luma   
  35.   numNoSao[1] = 0;// Chroma   
  36.   if( depth > 0 && m_depthSaoRate[0][depth-1] > SAO_ENCODING_RATE )  
  37.   {  
  38.     saoParam->bSaoFlag[0] = false;  
  39.   }  
  40.   if( depth > 0 && m_depthSaoRate[1][depth-1] > SAO_ENCODING_RATE_CHROMA )  
  41.   {  
  42.     saoParam->bSaoFlag[1] = false;  
  43.   }  
  44. #else  
  45.   Int numNoSao = 0;  
  46.   
  47.   if( depth > 0 && m_depth0SaoRate > SAO_ENCODING_RATE )  
  48.   {  
  49.     saoParam->bSaoFlag[0] = false;  
  50.     saoParam->bSaoFlag[1] = false;  
  51.   }  
  52. #endif  
  53. #endif  
  54.   //!< 以LCU为单位对图像中的每个LCU进行遍历  
  55.   for (idxY = 0; idxY< frameHeightInCU; idxY++)  
  56.   {  
  57.     for (idxX = 0; idxX< frameWidthInCU; idxX++)  
  58.     {  
  59.       addr     = idxX  + frameWidthInCU*idxY; //!< 当前LCU地址  
  60.       addrUp   = addr < frameWidthInCU ? -1:idxX   + frameWidthInCU*(idxY-1); //!< 当前LCU上邻块地址  
  61.       addrLeft = idxX == 0               ? -1:idxX-1 + frameWidthInCU*idxY; //!< 当前LCU左邻块地址  
  62.       Int allowMergeLeft = 1;  
  63.       Int allowMergeUp   = 1;  
  64.       UInt rate;  
  65.       Double bestCost, mergeCost;  
  66.       if (idxX!=0) //!< 非第1列  
  67.       {   
  68.         // check tile id and slice id //! 检查当前LCU与其左邻块是否属于同一个tile以及是否属于同一个slice,不同的话该邻块不可用  
  69.         if ( (m_pcPic->getPicSym()->getTileIdxMap(addr-1) != m_pcPic->getPicSym()->getTileIdxMap(addr)) || (m_pcPic->getCU(addr-1)->getSlice()->getSliceIdx() != m_pcPic->getCU(addr)->getSlice()->getSliceIdx()))  
  70.         {  
  71.           allowMergeLeft = 0; //!< 左邻块不可用  
  72.         }  
  73.       }  
  74.       else  
  75.       {  
  76.         allowMergeLeft = 0; //!< 第1列的左邻块均不可用  
  77.       }  
  78.       if (idxY!=0) //!< 非第1行  
  79.       {//! 检查当前LCU与其上邻块是否属于同一个tile以及是否属于同一个slice,不同的话该邻块不可用  
  80.         if ( (m_pcPic->getPicSym()->getTileIdxMap(addr-m_iNumCuInWidth) != m_pcPic->getPicSym()->getTileIdxMap(addr)) || (m_pcPic->getCU(addr-m_iNumCuInWidth)->getSlice()->getSliceIdx() != m_pcPic->getCU(addr)->getSlice()->getSliceIdx()))  
  81.         {  
  82.           allowMergeUp = 0; //!< 上邻块不可用  
  83.         }  
  84.       }  
  85.       else  
  86.       {  
  87.         allowMergeUp = 0; //!< 第1行的上邻块均不可用  
  88.       }  
  89.   
  90.       compDistortion[0] = 0; //!< Y distortion  
  91.       compDistortion[1] = 0; //!< Cb distortion  
  92.       compDistortion[2] = 0; //!< Cr distortion  
  93.       m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_CURR_BEST]);  
  94.       if (allowMergeLeft)  
  95.       {  
  96.         m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(0); //!< 编码句法元素sao_merge_left_flag  
  97.       }  
  98.       if (allowMergeUp)  
  99.       {  
  100.         m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(0); //!< 编码句法元素sao_merge_up_flag  
  101.       }  
  102.       m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[0][CI_TEMP_BEST] );  
  103.       // reset stats Y, Cb, Cr  
  104.       for ( compIdx=0;compIdx<3;compIdx++)  
  105.       {  
  106.         for ( j=0;j<MAX_NUM_SAO_TYPE;j++)  
  107.         {  
  108.           for ( k=0;k< MAX_NUM_SAO_CLASS;k++)  
  109.           {  
  110.             m_iOffset   [compIdx][j][k] = 0;  
  111.             if( m_saoLcuBasedOptimization && m_saoLcuBoundary ){ //!< true && false  
  112.               m_iCount    [compIdx][j][k] = m_count_PreDblk    [addr][compIdx][j][k];  
  113.               m_iOffsetOrg[compIdx][j][k] = m_offsetOrg_PreDblk[addr][compIdx][j][k];  
  114.             }  
  115.             else  
  116.             {  
  117.               m_iCount    [compIdx][j][k] = 0;  
  118.               m_iOffsetOrg[compIdx][j][k] = 0;  
  119.             }  
  120.           }    
  121.         }  
  122.         saoParam->saoLcuParam[compIdx][addr].typeIdx       =  -1;  
  123.         saoParam->saoLcuParam[compIdx][addr].mergeUpFlag   = 0;  
  124.         saoParam->saoLcuParam[compIdx][addr].mergeLeftFlag = 0;  
  125.         saoParam->saoLcuParam[compIdx][addr].subTypeIdx    = 0;  
  126. #if SAO_ENCODING_CHOICE  
  127.   if( (compIdx ==0 && saoParam->bSaoFlag[0])|| (compIdx >0 && saoParam->bSaoFlag[1]) )  
  128. #endif  
  129.         {//! 统计BO和EO各个模式下,对应classIdx下滤波前的重建像素值与原始像素值的差值的总和,以及对classIdx的计数  
  130.           calcSaoStatsCu(addr, compIdx,  compIdx);  
  131.         }  
  132.       }  
  133.       //!< Y分量最佳滤波模式的选择  
  134.       saoComponentParamDist(allowMergeLeft, allowMergeUp, saoParam, addr, addrUp, addrLeft, 0,  lambda, &mergeSaoParam[0][0], &compDistortion[0]);  
  135.       //!< CbCr分量最佳滤波模式的选择  
  136.       sao2ChromaParamDist(allowMergeLeft, allowMergeUp, saoParam, addr, addrUp, addrLeft, lambdaChroma, &mergeSaoParam[1][0], &mergeSaoParam[2][0], &compDistortion[0]);  
  137.      if( saoParam->bSaoFlag[0] || saoParam->bSaoFlag[1] )  
  138.       {  
  139.         // Cost of new SAO_params  
  140.         m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_CURR_BEST]);  
  141.         m_pcRDGoOnSbacCoder->resetBits();  
  142.         if (allowMergeLeft)  
  143.         {  
  144.           m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(0);   
  145.         }  
  146.         if (allowMergeUp)  
  147.         {  
  148.           m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(0);  
  149.         }  
  150.         for ( compIdx=0;compIdx<3;compIdx++)  
  151.         {  
  152.         if( (compIdx ==0 && saoParam->bSaoFlag[0]) || (compIdx >0 && saoParam->bSaoFlag[1]))  
  153.           {  
  154.            m_pcEntropyCoder->encodeSaoOffset(&saoParam->saoLcuParam[compIdx][addr], compIdx);  
  155.           }  
  156.         }  
  157.   
  158.         rate = m_pcEntropyCoder->getNumberOfWrittenBits();  
  159.         bestCost = compDistortion[0] + (Double)rate;  
  160.         m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[0][CI_TEMP_BEST]);  
  161.   
  162.         // Cost of Merge  
  163.         for(Int mergeUp=0; mergeUp<2; ++mergeUp)  
  164.         {  
  165.           if ( (allowMergeLeft && (mergeUp==0)) || (allowMergeUp && (mergeUp==1)) )  
  166.           {  
  167.             m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_CURR_BEST]);  
  168.             m_pcRDGoOnSbacCoder->resetBits();  
  169.             if (allowMergeLeft)  
  170.             {  
  171.               m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(1-mergeUp);   
  172.             }  
  173.             if ( allowMergeUp && (mergeUp==1) )  
  174.             {  
  175.               m_pcEntropyCoder->m_pcEntropyCoderIf->codeSaoMerge(1);   
  176.             }  
  177.   
  178.             rate = m_pcEntropyCoder->getNumberOfWrittenBits();  
  179.             mergeCost = compDistortion[mergeUp+1] + (Double)rate;  
  180.             if (mergeCost < bestCost)  
  181.             {  
  182.               bestCost = mergeCost;  
  183.               m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[0][CI_TEMP_BEST]);                
  184.               for ( compIdx=0;compIdx<3;compIdx++)  
  185.               {  
  186.                 mergeSaoParam[compIdx][mergeUp].mergeLeftFlag = 1-mergeUp;  
  187.                 mergeSaoParam[compIdx][mergeUp].mergeUpFlag = mergeUp;  
  188.                 if( (compIdx==0 && saoParam->bSaoFlag[0]) || (compIdx>0 && saoParam->bSaoFlag[1]))  
  189.                 {  
  190.                   copySaoUnit(&saoParam->saoLcuParam[compIdx][addr], &mergeSaoParam[compIdx][mergeUp] );               
  191.                 }  
  192.               }  
  193.             }  
  194.           }  
  195.         }  
  196. #if SAO_ENCODING_CHOICE  
  197. #if SAO_ENCODING_CHOICE_CHROMA  
  198. if( saoParam->saoLcuParam[0][addr].typeIdx == -1) //!< Y分量不存在SAO参数  
  199. {  
  200.   numNoSao[0]++;  
  201. }  
  202. if( saoParam->saoLcuParam[1][addr].typeIdx == -1) //!< CbCr分量不存在SAO参数  
  203. {  
  204.   numNoSao[1]+=2;  
  205. }  
  206. #else  
  207.         for ( compIdx=0;compIdx<3;compIdx++)  
  208.         {  
  209.           if( depth == 0 && saoParam->saoLcuParam[compIdx][addr].typeIdx == -1)  
  210.           {  
  211.             numNoSao++;  
  212.           }  
  213.         }  
  214. #endif  
  215. #endif  
  216.         m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_TEMP_BEST]);  
  217.         m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[0][CI_CURR_BEST]);  
  218.       } //!< if( saoParam->bSaoFlag[0] || saoParam->bSaoFlag[1] )         
  219.     } //!< for (idxX = 0; idxX< frameWidthInCU; idxX++)       
  220.   } //!< for (idxY = 0; idxY< frameHeightInCU; idxY++)     
  221. #if SAO_ENCODING_CHOICE  
  222. #if SAO_ENCODING_CHOICE_CHROMA  
  223. #if SAO_ENCODING_CHOICE_CHROMA_BF  
  224.   if( !saoParam->bSaoFlag[0])   
  225.   {  
  226.     m_depthSaoRate[0][depth] = 1.0;  
  227.   }  
  228.   else  
  229.   {  
  230.     m_depthSaoRate[0][depth] = numNoSao[0]/((Double) frameHeightInCU*frameWidthInCU);  
  231.   }  
  232.   if( !saoParam->bSaoFlag[1])   
  233.   {  
  234.     m_depthSaoRate[1][depth] = 1.0;  
  235.   }  
  236.   else   
  237.   {  
  238.     m_depthSaoRate[1][depth] = numNoSao[1]/((Double) frameHeightInCU*frameWidthInCU*2);  
  239.   }  
  240. #else  
  241. m_depthSaoRate[0][depth] = numNoSao[0]/((Double) frameHeightInCU*frameWidthInCU);  
  242. m_depthSaoRate[1][depth] = numNoSao[1]/((Double) frameHeightInCU*frameWidthInCU*2);  
  243. #endif  
  244. #else  
  245.   if( depth == 0)  
  246.   {  
  247.     // update SAO Rate  
  248.     m_depth0SaoRate = numNoSao/((Double) frameHeightInCU*frameWidthInCU*3);  
  249.   }  
  250. #endif  
  251. #endif  
  252.   
  253. }  

(三)http://blog.csdn.net/hevc_cjl/article/details/8288584

完成SAO论文中V-D小节所讲述内容中 公式(7)中所述的N和E的数值的计算,

其中E对应变量iStats,而N对应变量iCount所指向的数组元素

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** Calculate SAO statistics for current LCU 
  2.  * \param  iAddr,  iPartIdx,  iYCbCr 
  3.  */  
  4. Void TEncSampleAdaptiveOffset::calcSaoStatsCu(Int iAddr, Int iPartIdx, Int iYCbCr)  
  5. {  
  6.   if(!m_bUseNIF) //!< true for performing non-cross slice boundary ALF  
  7.   {  
  8.     calcSaoStatsCuOrg( iAddr, iPartIdx, iYCbCr);  // 没有多 slice 划分的情况,else中处理多slice的情况  
  9.   }  
  10.   else   
  11.   {  
  12.     Int64** ppStats = m_iOffsetOrg[iPartIdx]; //!< [MAX_NUM_SAO_PART][MAX_NUM_SAO_TYPE][MAX_NUM_SAO_CLASS] ??  
  13.     Int64** ppCount = m_iCount    [iPartIdx]; //!< [MAX_NUM_SAO_PART][MAX_NUM_SAO_TYPE][MAX_NUM_SAO_CLASS]  
  14.   
  15.     //parameters  
  16.     Int  isChroma = (iYCbCr != 0)? 1:0;  
  17.     Int  stride   = (iYCbCr != 0)?(m_pcPic->getCStride()):(m_pcPic->getStride());  
  18.     Pel* pPicOrg = getPicYuvAddr (m_pcPic->getPicYuvOrg(), iYCbCr);  
  19.     Pel* pPicRec  = getPicYuvAddr(m_pcYuvTmp, iYCbCr);  
  20.   
  21.     std::vector<NDBFBlockInfo>& vFilterBlocks = *(m_pcPic->getCU(iAddr)->getNDBFilterBlocks());  
  22.   
  23.     //variables  
  24.     UInt  xPos, yPos, width, height;  
  25.     Bool* pbBorderAvail;  
  26.     UInt  posOffset;  
  27.   
  28.     for(Int i=0; i< vFilterBlocks.size(); i++)  
  29.     {  
  30.       xPos        = vFilterBlocks[i].posX   >> isChroma;  
  31.       yPos        = vFilterBlocks[i].posY   >> isChroma;  
  32.       width       = vFilterBlocks[i].width  >> isChroma;  
  33.       height      = vFilterBlocks[i].height >> isChroma;  
  34.       pbBorderAvail = vFilterBlocks[i].isBorderAvailable;  
  35.   
  36.       posOffset = (yPos* stride) + xPos;  
  37.       //! 对ppStats,ppCount赋值,分别计算出对应滤波模式下原始像素与重建像素之间的差值,重建值对应的classIdx的统计值  
  38.       calcSaoStatsBlock(pPicRec+ posOffset, pPicOrg+ posOffset, stride, ppStats, ppCount,width, height, pbBorderAvail, iYCbCr);  
  39.     }  
  40.   }  
  41.   
  42. }  
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** Calculate SAO statistics for non-cross-slice or non-cross-tile processing 
  2.  * \param  pRecStart to-be-filtered block buffer pointer 
  3.  * \param  pOrgStart original block buffer pointer 
  4.  * \param  stride picture buffer stride 
  5.  * \param  ppStat statistics buffer 
  6.  * \param  ppCount counter buffer 
  7.  * \param  width block width 
  8.  * \param  height block height 
  9.  * \param  pbBorderAvail availabilities of block border pixels 
  10.  */  
  11. Void TEncSampleAdaptiveOffset::calcSaoStatsBlock( Pel* pRecStart, Pel* pOrgStart, Int stride, Int64** ppStats, Int64** ppCount, UInt width, UInt height, Bool* pbBorderAvail, Int iYCbCr)  
  12. {  
  13.   Int64 *stats, *count;  
  14.   Int classIdx, posShift, startX, endX, startY, endY, signLeft,signRight,signDown,signDown1;  
  15.   Pel *pOrg, *pRec;  
  16.   UInt edgeType;  
  17.   Int x, y;  
  18.   Pel *pTableBo = (iYCbCr==0)?m_lumaTableBo:m_chromaTableBo; //!< band offset 的索引表,共32个bands  
  19.   
  20.   //--------- Band offset (对应SAO论文中第III节讲述的BO)-----------//  
  21.   stats = ppStats[SAO_BO];  
  22.   count = ppCount[SAO_BO];  
  23.   pOrg   = pOrgStart;  
  24.   pRec   = pRecStart;  
  25.   for (y=0; y< height; y++)  
  26.   {  
  27.     for (x=0; x< width; x++)  
  28.     {  
  29.       classIdx = pTableBo[pRec[x]]; //!< classIdx即查表得到的band对应的序号值(1~32)  
  30.       if (classIdx)  
  31.       {  
  32.         stats[classIdx] += (pOrg[x] - pRec[x]); //!< 对原始像素与重建像素的差值求和  
  33.         count[classIdx] ++; //!< 对应classIdx的统计值加1  
  34.       }  
  35.     }  
  36.     pOrg += stride;  
  37.     pRec += stride;  
  38.   }  
  39.   //---------- Edge offset 0 (对应SAO论文中第III节中EO的class划分中第一类,一下 SAO_EO_1 等类似)--------------//  
  40.   stats = ppStats[SAO_EO_0];  
  41.   count = ppCount[SAO_EO_0];  
  42.   pOrg   = pOrgStart;  
  43.   pRec   = pRecStart;  
  44.   
  45.   //!< 设置起始点和终点  
  46.   startX = (pbBorderAvail[SGU_L]) ? 0 : 1;  
  47.   endX   = (pbBorderAvail[SGU_R]) ? width : (width -1);  
  48.   for (y=0; y< height; y++)  
  49.   {  
  50.     signLeft = xSign(pRec[startX] - pRec[startX-1]); //!< 取 p - n0 的符号  
  51.     for (x=startX; x< endX; x++)  
  52.     {  
  53.       signRight =  xSign(pRec[x] - pRec[x+1]); //!< 取 p - n1 的符号  
  54.       edgeType =  signRight + signLeft + 2; //!< 计算符号类型,用于下面通过查表的方式确定EdgeIdx  
  55.       signLeft  = -signRight;  // 这里往右遍历时右边的点可以利用左边邻近点的边界信息,减少了计算量  
  56.   
  57.       /* 
  58.       const UInt TComSampleAdaptiveOffset::m_auiEoTable[9] = { 
  59.       1, //0     
  60.       2, //1    
  61.       0, //2 
  62.       3, //3 
  63.       4, //4 
  64.       0, //5   
  65.       0, //6   
  66.       0, //7  
  67.       0}; 
  68.       */  
  69.       stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]); //!< 通过查表可以确定出真正的EdgeIdx,从而把统计值保存到对应的类型中去  
  70.       count[m_auiEoTable[edgeType]] ++;  
  71.     }  
  72.     pRec  += stride;  
  73.     pOrg += stride;  
  74.   }  
  75.   
  76.   //---------- Edge offset 1--------------//  
  77.   stats = ppStats[SAO_EO_1];  
  78.   count = ppCount[SAO_EO_1];  
  79.   pOrg   = pOrgStart;  
  80.   pRec   = pRecStart;  
  81.   
  82.   startY = (pbBorderAvail[SGU_T]) ? 0 : 1;  
  83.   endY   = (pbBorderAvail[SGU_B]) ? height : height-1;  
  84.   if (!pbBorderAvail[SGU_T]) //!< 如果上邻行不可用,则下移一行  
  85.   {  
  86.     pRec  += stride;  
  87.     pOrg  += stride;  
  88.   }  
  89.   
  90.   for (x=0; x< width; x++) //!< 先计算第一行与其上一行的差值  
  91.   {  
  92.     m_iUpBuff1[x] = xSign(pRec[x] - pRec[x-stride]); //!< 保存整一行 p - n0 的符号  
  93.   }  
  94.   for (y=startY; y<endY; y++)  
  95.   {  
  96.     for (x=0; x< width; x++)  
  97.     {  
  98.       signDown     =  xSign(pRec[x] - pRec[x+stride]); //!< 取 p - n1 的符号  
  99.       edgeType    =  signDown + m_iUpBuff1[x] + 2;  
  100.       m_iUpBuff1[x] = -signDown; //!< -signDown相当于是下一行的 p - n0 的符号,保存下来  
  101.   
  102.       stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  103.       count[m_auiEoTable[edgeType]] ++;  
  104.     }  
  105.     pOrg += stride;  
  106.     pRec += stride;  
  107.   }  
  108.   //---------- Edge offset 2--------------//  
  109.   stats = ppStats[SAO_EO_2];  
  110.   count = ppCount[SAO_EO_2];  
  111.   pOrg   = pOrgStart;  
  112.   pRec   = pRecStart;  
  113.   
  114.   posShift= stride + 1;  
  115.   
  116.   startX = (pbBorderAvail[SGU_L]) ? 0 : 1 ;  
  117.   endX   = (pbBorderAvail[SGU_R]) ? width : (width-1);  
  118.   
  119.   //prepare 2nd line upper sign  
  120.   pRec += stride;  
  121.   for (x=startX; x< endX+1; x++) //!< 先计算第二行的 p - n0 的符号,并保存下来  
  122.   {  
  123.     m_iUpBuff1[x] = xSign(pRec[x] - pRec[x- posShift]);  
  124.   }  
  125.   
  126.   //1st line  
  127.   pRec -= stride; //!< 回到第一行  
  128.   if(pbBorderAvail[SGU_TL]) //!< Top Left available  
  129.   {  
  130.     x= 0;  
  131.     edgeType =  xSign(pRec[x] - pRec[x- posShift]) - m_iUpBuff1[x+1] + 2; //!< -m_iUpBuff1[x+1]是因为第二行的p - n0相当于第一行的n1 - p,且第二行的x+1的位置才是第一行的x位置  
  132.     stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  133.     count[m_auiEoTable[edgeType]] ++;  
  134.   }  
  135.   if(pbBorderAvail[SGU_T]) //!< Top available  
  136.   {  
  137.     for(x= 1; x< endX; x++)  
  138.     {  
  139.       edgeType      =  xSign(pRec[x] - pRec[x- posShift]) - m_iUpBuff1[x+1] + 2;  
  140.       stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  141.       count[m_auiEoTable[edgeType]] ++;  
  142.     }  
  143.   }  
  144.   pRec   += stride;  
  145.   pOrg   += stride;  
  146.   
  147.   //middle lines  
  148.   for (y= 1; y< height-1; y++) //!< 除了第一行和最后一行的行  
  149.   {  
  150.     for (x=startX; x<endX; x++)  
  151.     {  
  152.       signDown1      =  xSign(pRec[x] - pRec[x+ posShift]) ; //!< 取 p - n1 的符号  
  153.       edgeType      =  signDown1 + m_iUpBuff1[x] + 2; //!< 此时m_iUpBuff1[x]正好就是p - n0 的符号  
  154.       stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  155.       count[m_auiEoTable[edgeType]] ++;  
  156.   
  157.       m_iUpBufft[x+1] = -signDown1; //!< 当前行的p - n1相当于下一行的p - n0 的相反数,且当前行的x,对应于下一行的x+1  
  158.     }  
  159.     m_iUpBufft[startX] = xSign(pRec[stride+startX] - pRec[startX-1]); //!< 取startX位置的p - n0的符号  
  160.     //!< m_iUpBuff1与m_iUpBufft交换,交换完成后,m_iUpBuff1将保存的是下一行的p - n0的符号  
  161.     ipSwap     = m_iUpBuff1;  
  162.     m_iUpBuff1 = m_iUpBufft;  
  163.     m_iUpBufft = ipSwap;  
  164.   
  165.     pRec  += stride;  
  166.     pOrg  += stride;  
  167.   }  
  168.   
  169.   //last line  
  170.   if(pbBorderAvail[SGU_B]) //!< 最后一行,Bottom available  
  171.   {  
  172.     for(x= startX; x< width-1; x++)  
  173.     {  
  174.       edgeType =  xSign(pRec[x] - pRec[x+ posShift]) + m_iUpBuff1[x] + 2; //!< 此时m_iUpBuff1保存的就是最后一行的p - n0的符号  
  175.       stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  176.       count[m_auiEoTable[edgeType]] ++;  
  177.     }  
  178.   }  
  179.   if(pbBorderAvail[SGU_BR]) //!< Bottom Right available  
  180.   {  
  181.     x= width -1;  
  182.     edgeType =  xSign(pRec[x] - pRec[x+ posShift]) + m_iUpBuff1[x] + 2;  
  183.     stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  184.     count[m_auiEoTable[edgeType]] ++;  
  185.   }  
  186.   
  187.   //---------- Edge offset 3--------------//  
  188.   
  189.   stats = ppStats[SAO_EO_3];  
  190.   count = ppCount[SAO_EO_3];  
  191.   pOrg   = pOrgStart;  
  192.   pRec   = pRecStart;  
  193.   
  194.   posShift     = stride - 1;  
  195.   startX = (pbBorderAvail[SGU_L]) ? 0 : 1;  
  196.   endX   = (pbBorderAvail[SGU_R]) ? width : (width -1);  
  197.   
  198.   //prepare 2nd line upper sign  
  199.   pRec += stride;  
  200.   for (x=startX-1; x< endX; x++) //!< 先算第二行的p - n0的符号并保存到m_iUpBuff1中  
  201.   {  
  202.     m_iUpBuff1[x] = xSign(pRec[x] - pRec[x- posShift]);  
  203.   }  
  204.   
  205.   
  206.   //first line  
  207.   pRec -= stride; //!< 回到第一行  
  208.   if(pbBorderAvail[SGU_T]) //!< Top available  
  209.   {  
  210.     for(x= startX; x< width -1; x++)  
  211.     {  
  212.       edgeType = xSign(pRec[x] - pRec[x- posShift]) -m_iUpBuff1[x-1] + 2;//!< -m_iUpBuff1[x-1]是因为第二行的p - n0相当于第一行的n1 - p,且第二行的x-1的位置才是第一行的x位置  
  213.       stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  214.       count[m_auiEoTable[edgeType]] ++;  
  215.     }  
  216.   }  
  217.   if(pbBorderAvail[SGU_TR]) //!< Top Right available  
  218.   {  
  219.     x= width-1;  
  220.     edgeType = xSign(pRec[x] - pRec[x- posShift]) -m_iUpBuff1[x-1] + 2;  
  221.     stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  222.     count[m_auiEoTable[edgeType]] ++;  
  223.   }  
  224.   pRec  += stride;  
  225.   pOrg  += stride;  
  226.   
  227.   //middle lines  
  228.   for (y= 1; y< height-1; y++) //!< 除第一行和最后一行的行  
  229.   {  
  230.     for(x= startX; x< endX; x++)  
  231.     {  
  232.       signDown1      =  xSign(pRec[x] - pRec[x+ posShift]) ;  
  233.       edgeType      =  signDown1 + m_iUpBuff1[x] + 2; //!< 此时m_iUpBuff1就是当前行的p - n0的符号  
  234.   
  235.       stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  236.       count[m_auiEoTable[edgeType]] ++;  
  237.       m_iUpBuff1[x-1] = -signDown1; //!< 当前行的p - n1相当于下一行的p - n0 的相反数,且当前行的x,对应于下一行的x-1  
  238.   
  239.     }  
  240.     m_iUpBuff1[endX-1] = xSign(pRec[endX-1 + stride] - pRec[endX]); //!< 保存下一行endX-1处的p - n0的符号  
  241.   
  242.     pRec  += stride;  
  243.     pOrg  += stride;  
  244.   }  
  245.   
  246.   //last line  
  247.   if(pbBorderAvail[SGU_BL]) //!< Bottom Left available  
  248.   {  
  249.     x= 0;  
  250.     edgeType = xSign(pRec[x] - pRec[x+ posShift]) + m_iUpBuff1[x] + 2; //!< 此时m_iUpBuff1正好保存的是最后一行的x位置的p - n0的符号  
  251.     stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  252.     count[m_auiEoTable[edgeType]] ++;  
  253.   
  254.   }  
  255.   if(pbBorderAvail[SGU_B]) //!< Bottom available  
  256.   {  
  257.     for(x= 1; x< endX; x++)  
  258.     {  
  259.       edgeType = xSign(pRec[x] - pRec[x+ posShift]) + m_iUpBuff1[x] + 2;  
  260.       stats[m_auiEoTable[edgeType]] += (pOrg[x] - pRec[x]);  
  261.       count[m_auiEoTable[edgeType]] ++;  
  262.     }  
  263.   }  
  264. }  

(四) http://blog.csdn.net/hevc_cjl/article/details/8288601

亮度分量上最优SAO参数的选取,色度分量的流程基本上是相似的,因而原作者没有给出注解。

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. Void TEncSampleAdaptiveOffset::saoComponentParamDist(Int allowMergeLeft, Int allowMergeUp, SAOParam *saoParam, Int addr, Int addrUp, Int addrLeft, Int yCbCr, Double lambda, SaoLcuParam *compSaoParam, Double *compDistortion)  
  2. {  
  3.   Int typeIdx;  
  4.   
  5.   Int64 estDist;  
  6.   Int classIdx;  
  7.   Int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(((yCbCr==0)?g_bitDepthY:g_bitDepthC)-8); //!< 0 for 8bit-depth  
  8.   Int64 bestDist;  
  9.   
  10.   SaoLcuParam*  saoLcuParam = &(saoParam->saoLcuParam[yCbCr][addr]);  
  11.   SaoLcuParam*  saoLcuParamNeighbor = NULL;   
  12.   
  13.   resetSaoUnit(saoLcuParam);  
  14.   resetSaoUnit(&compSaoParam[0]); //!< 左邻块的SAO参数  
  15.   resetSaoUnit(&compSaoParam[1]); //!< 上邻块的SAO参数  
  16.   
  17.   
  18.   Double dCostPartBest = MAX_DOUBLE;  
  19.   
  20.   Double  bestRDCostTableBo = MAX_DOUBLE;  
  21.   Int     bestClassTableBo    = 0;  
  22.   Int     currentDistortionTableBo[MAX_NUM_SAO_CLASS];  
  23.   Double  currentRdCostTableBo[MAX_NUM_SAO_CLASS];  
  24.   
  25.   
  26.   SaoLcuParam   saoLcuParamRdo;     
  27.   Double   estRate = 0;  
  28.   
  29.   resetSaoUnit(&saoLcuParamRdo);  
  30.   
  31.   m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_TEMP_BEST]);  
  32.   m_pcRDGoOnSbacCoder->resetBits();  
  33.  m_pcEntropyCoder->encodeSaoOffset(&saoLcuParamRdo, yCbCr);  
  34.     
  35.   dCostPartBest = m_pcEntropyCoder->getNumberOfWrittenBits()*lambda ;   
  36.   copySaoUnit(saoLcuParam, &saoLcuParamRdo );  
  37.   bestDist = 0;  
  38.     
  39.   
  40.   
  41.   for (typeIdx=0; typeIdx<MAX_NUM_SAO_TYPE; typeIdx++) //!< 遍历所有的滤波类型  
  42.   {  
  43.     estDist = estSaoTypeDist(yCbCr, typeIdx, shift, lambda, currentDistortionTableBo, currentRdCostTableBo);//!< 得到当前滤波类型的失真值  
  44.   
  45.     if( typeIdx == SAO_BO )  
  46.     {  
  47.       // Estimate Best Position  
  48.       Double currentRDCost = 0.0;  
  49.   
  50.       for(Int i=0; i< SAO_MAX_BO_CLASSES -SAO_BO_LEN +1; i++)  
  51.       {  
  52.         currentRDCost = 0.0;  
  53.         for(UInt uj = i; uj < i+SAO_BO_LEN; uj++) //!< 依次以4个band为单位进行RDcost计算  
  54.         {  
  55.           currentRDCost += currentRdCostTableBo[uj];  
  56.         }  
  57.   
  58.         if( currentRDCost < bestRDCostTableBo) //!< 更新最佳值  
  59.         {  
  60.           bestRDCostTableBo = currentRDCost;  
  61.           bestClassTableBo  = i;  
  62.         }  
  63.       }  
  64.   
  65.       // Re code all Offsets  
  66.       // Code Center  
  67.       estDist = 0;  
  68.       for(classIdx = bestClassTableBo; classIdx < bestClassTableBo+SAO_BO_LEN; classIdx++)   
  69.       {  
  70.         estDist += currentDistortionTableBo[classIdx];  
  71.       }  
  72.     } //!< if( typeIdx == SAO_BO )  
  73.     resetSaoUnit(&saoLcuParamRdo);  
  74.     saoLcuParamRdo.length = m_iNumClass[typeIdx];  
  75.     saoLcuParamRdo.typeIdx = typeIdx;  
  76.     saoLcuParamRdo.mergeLeftFlag = 0;  
  77.     saoLcuParamRdo.mergeUpFlag   = 0;  
  78.     saoLcuParamRdo.subTypeIdx = (typeIdx == SAO_BO) ? bestClassTableBo : 0;  
  79.     for (classIdx = 0; classIdx < saoLcuParamRdo.length; classIdx++)  
  80.     {  
  81.       saoLcuParamRdo.offset[classIdx] = (Int)m_iOffset[yCbCr][typeIdx][classIdx+saoLcuParamRdo.subTypeIdx+1];  
  82.     }  
  83.     m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_TEMP_BEST]);  
  84.     m_pcRDGoOnSbacCoder->resetBits();  
  85.     m_pcEntropyCoder->encodeSaoOffset(&saoLcuParamRdo, yCbCr);  
  86.   
  87.     estRate = m_pcEntropyCoder->getNumberOfWrittenBits();  
  88.     m_dCost[yCbCr][typeIdx] = (Double)((Double)estDist + lambda * (Double) estRate);  
  89.   
  90.     if(m_dCost[yCbCr][typeIdx] < dCostPartBest) //!< 更新最佳值  
  91.     {  
  92.       dCostPartBest = m_dCost[yCbCr][typeIdx];  
  93.       copySaoUnit(saoLcuParam, &saoLcuParamRdo );  
  94.       bestDist = estDist;         
  95.     }  
  96.   } //!< for (typeIdx=0; typeIdx<MAX_NUM_SAO_TYPE; typeIdx++)  
  97.   compDistortion[0] += ((Double)bestDist/lambda);  
  98.   m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[0][CI_TEMP_BEST]);  
  99.  m_pcEntropyCoder->encodeSaoOffset(saoLcuParam, yCbCr);  
  100.   m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[0][CI_TEMP_BEST] );  
  101.   
  102.   
  103.   // merge left or merge up  
  104.   
  105.   for (Int idxNeighbor=0;idxNeighbor<2;idxNeighbor++)   
  106.   {  
  107.     saoLcuParamNeighbor = NULL;  
  108.     if (allowMergeLeft && addrLeft>=0 && idxNeighbor ==0) //!< 左邻块可用  
  109.     {  
  110.       saoLcuParamNeighbor = &(saoParam->saoLcuParam[yCbCr][addrLeft]); //!< 取左邻块的SAO参数  
  111.     }  
  112.     else if (allowMergeUp && addrUp>=0 && idxNeighbor ==1) //!< 上邻块可用  
  113.     {  
  114.       saoLcuParamNeighbor = &(saoParam->saoLcuParam[yCbCr][addrUp]); //!< 取上邻块的SAO参数  
  115.     }  
  116.     if (saoLcuParamNeighbor!=NULL)  
  117.     {  
  118.       estDist = 0;  
  119.       typeIdx = saoLcuParamNeighbor->typeIdx;  
  120.       if (typeIdx>=0) //!< typeIdx为有效的   
  121.       {  
  122.         Int mergeBandPosition = (typeIdx == SAO_BO)?saoLcuParamNeighbor->subTypeIdx:0;  
  123.         Int   merge_iOffset;  
  124.         for(classIdx = 0; classIdx < m_iNumClass[typeIdx]; classIdx++)  
  125.         {  
  126.           merge_iOffset = saoLcuParamNeighbor->offset[classIdx];  
  127.           estDist   += estSaoDist(m_iCount [yCbCr][typeIdx][classIdx+mergeBandPosition+1], merge_iOffset, m_iOffsetOrg[yCbCr][typeIdx][classIdx+mergeBandPosition+1],  shift);  
  128.         }  
  129.       }  
  130.       else  
  131.       {  
  132.         estDist = 0;  
  133.       }  
  134.   
  135.       copySaoUnit(&compSaoParam[idxNeighbor], saoLcuParamNeighbor );  
  136.       compSaoParam[idxNeighbor].mergeUpFlag   = idxNeighbor;  
  137.       compSaoParam[idxNeighbor].mergeLeftFlag = !idxNeighbor;  
  138.   
  139.       compDistortion[idxNeighbor+1] += ((Double)estDist/lambda);  
  140.     }   
  141.   }   
  142. }  
(五) 附上一个xSign函数的解析http://blog.csdn.net/hevc_cjl/article/details/8290370

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. inline Int xSign(Int x) //!< 取出x的符号,x大于0返回+1,x小于0返回-1  
  2. {//! 当x等于0时,返回0;当x小于0时,由于x是int型,x>>31(算术右移)后结果为0xffffffff,即-1,而-x为0x00000001,右移31位后结果为0,  
  3.   //! 因此此时返回值为-1;当x大于0时,x>>31后结果为0x00000000,即0,而-x即为补码形式表示的负数(最高位为1),被转换为unsigned int后,  
  4.   //! 再右移31位时,符号位不会保留,最终移位结果为0x00000001,即1  
  5.   return ((x >> 31) | ((Int)( (((UInt) -x)) >> 31)));  
  6. }  

这篇关于HEVC中SAO--自适应样点补偿 分析解读的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/702278

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断