HM编码器代码阅读(27)——样点自适应补偿SAO(一)SAO介绍以及入口函数
2017-02-12 20:49
459 查看
样点自适应补偿(SAO)
原理
介绍
由于编码过程中使用了变换量化,这会导致高频交流系数失真,所以解码后会在图像的边缘产生波纹现象,这种被称为振铃效应。样点自适应补偿就是从像素域入手降低振铃效应:对重构曲线(通过对像素值进行划分分类可以得到)中出现的波峰像素添加负值补偿,波谷添加正值进行补偿。SAO以CTU为基本单位,通过选择一个合适的分类器将冲击的像素划分类别,然后对不同类别像素使用不同的补偿值。SAO包括两大类的补偿方式:边界补偿(EO)和边带补偿(BO),此外,还引入了参数融合技术。
边界补偿EO
它通过比较当前像素值与相邻像素值的大小对当前像素进行归类,然后对同类像素补偿相同数值。边界补偿共有四种模式:水平方向(EO_0)、垂直方向(EO_1)、135度方向(EO_2)以及45度(EO_3)。在任意一种模式下,都可以把像素归为下面的5种类型:(1)波谷——种类1
(2)下坡波谷——种类2
(3)上坡波谷——种类2
(4)下坡波峰——种类3
(5)上坡波峰——种类3
(6)波峰——种类4
(7)其他的——种类0
对于种类1、2、3、4都需要进行补偿,对于种类0不进行补偿,同一种类的像素要使用相同的补偿值
边带补偿BO
它根据像素强度值进行归类,它将像素范围分为32条边带。然后每一个边带会根据吱声像素特点进行补偿,且同意边带使用相同的补偿值。一般情况下,在一定的图像区域内,像素值的波动范围很小,HEVC规定一个CTU只能选择4条连续的边带,并且只对属于这四个边带的像素进行补偿,选择哪4条边带可以通过率失真优化方法确定
SAO参数融合-merge
参数融合是指对一个CTU,它的SAO参数直接使用相邻块的SAO参数,这时只需要标识采用了哪个相邻块的SAO参数即可。当前块的SAO参数有下面的三种选择:(1)直接使用左边的块
(2)直接使用上面的块
(3)通过分析自己像素块的特点,选择自己的参数(即使用EO或BO)
注意:一个CTU的亮度块和色度块分量必须同时使用左边或者上面的相邻块的补偿参数,否则使用EO或者BO
更多的信息请看 HEVC/H.265理论知识(7)——环路滤波
SAO的数据结构和枚举
// SAO所有的模式 typedef enum { SAO_MODE_OFF = 0, SAO_MODE_NEW, SAO_MODE_MERGE, NUM_SAO_MODES }SAOMode;
// SAO的merge模式的细分 typedef enum { SAO_MERGE_LEFT =0, SAO_MERGE_ABOVE, NUM_SAO_MERGE_TYPES }SAOModeMergeTypes;
// EO、BO的详细分类 typedef enum { SAO_TYPE_START_EO =0, SAO_TYPE_EO_0 = SAO_TYPE_START_EO, SAO_TYPE_EO_90, SAO_TYPE_EO_135, SAO_TYPE_EO_45, SAO_TYPE_START_BO, SAO_TYPE_BO = SAO_TYPE_START_BO, NUM_SAO_NEW_TYPES }SAOModeNewTypes;
// 使用EO的时候,像素的分类 typedef enum { SAO_CLASS_EO_FULL_VALLEY = 0, SAO_CLASS_EO_HALF_VALLEY = 1, SAO_CLASS_EO_PLAIN = 2, SAO_CLASS_EO_HALF_PEAK = 3, SAO_CLASS_EO_FULL_PEAK = 4, NUM_SAO_EO_CLASSES, }SAOEOClasses;
/* ** sao的统计数据 ** 保存了CTU(或者像素块)中像素的统计信息 */ struct SAOStatData //data structure for SAO statistics { Int64 diff[MAX_NUM_SAO_CLASSES]; Int64 count[MAX_NUM_SAO_CLASSES]; SAOStatData(){} ~SAOStatData(){} Void reset() { ::memset(diff, 0, sizeof(Int64)*MAX_NUM_SAO_CLASSES); ::memset(count, 0, sizeof(Int64)*MAX_NUM_SAO_CLASSES); } const SAOStatData& operator=(const SAOStatData& src) { ::memcpy(diff, src.diff, sizeof(Int64)*MAX_NUM_SAO_CLASSES); ::memcpy(count, src.count, sizeof(Int64)*MAX_NUM_SAO_CLASSES); return *this; } #if SAO_ENCODE_ALLOW_USE_PREDEBLOCK const SAOStatData& operator+= (const SAOStatData& src) { for(Int i=0; i< MAX_NUM_SAO_CLASSES; i++) { diff[i] += src.diff[i]; count[i] += src.count[i]; } return *this; } #endif };
/* ** 一个CTU分量的SAO参数信息 */ struct SAOOffset { // SAO的模式:关闭,new(即EO或者BO),merge Int modeIdc; //NEW, MERGE, OFF // 模式的细分:EO_0, EO_90, EO_135, EO_45, BO. MERGE: left, above Int typeIdc; //NEW: EO_0, EO_90, EO_135, EO_45, BO. MERGE: left, above // BO带的起始索引 Int typeAuxInfo; //BO: starting band index Int offset[MAX_NUM_SAO_CLASSES]; SAOOffset(); ~SAOOffset(); Void reset(); const SAOOffset& operator= (const SAOOffset& src); };
/* ** 一个CTU的SAO参数信息 */ struct SAOBlkParam { SAOBlkParam(); ~SAOBlkParam(); Void reset(); const SAOBlkParam& operator= (const SAOBlkParam& src); SAOOffset& operator[](Int compIdx){ return offsetParam[compIdx];} private: // 三个分量的SAO信息 SAOOffset offsetParam[NUM_SAO_COMPONENTS]; };
SAO的主函数
HEVC中SAO的主函数是TEncSampleAdaptiveOffset::SAOProcess,它的工作流程如下:1、调用getStatistics,收集图像中像素的统计信息,用于判断像素属于EO的哪个种类/BO的哪个边带对于图像中的每一个CTU:
(1)调用deriveLoopFilterBoundaryAvailibility,判断环路滤波是否允许跨越slice或者tile的边界,以此判断当前CTU的邻居是否可用
(2)对于三个颜色分量,分别调用getBlkStats,该函数是统计像素块中像素的特性,然后判断像素属于EO的哪一个种类
2、如果预先去方块滤波的标志为真,那么调用addPreDBFStatistics,这个函数的目的是在上一步的统计信息的基础上添加去方块滤波的一些统计信息
3、调用decidePicParams,判断像素块的三个颜色分量(Y、U、V)是否需要进行SAO操作
4、调用decideBlkParams,进行SAO处理。对于图像的每一个CTU,进行下面的处理:
(1)调用getMergeList,得到SAO的merge列表(这一步就是SAO的merge模式)
(2)按照下面的步骤分别处理EO、BO、Merge三种类型,然后选取出最优的一种
①如果是EO或者BO,那么调用deriveModeNewRDO,处理EO和BO的每一种模式,并选择最优的那种模式
②如果是merge,那么调用deriveModeMergeRDO,处理merge的每一种方式,选取最优的方式
③最后选择出所有模式中最优的那个
(3)利用最优的模式(EO、BO、Merge三者之一),调用reconstructBlkSAOParam和offsetCTU,对重建的CTU进行补偿
/* ** SAO主函数 */ Void TEncSampleAdaptiveOffset::SAOProcess(TComPic* pPic, Bool* sliceEnabled, const Double *lambdas #if SAO_ENCODE_ALLOW_USE_PREDEBLOCK , Bool isPreDBFSamplesUsed #endif ) { TComPicYuv* orgYuv= pPic->getPicYuvOrg(); TComPicYuv* resYuv= pPic->getPicYuvRec(); m_lambda[SAO_Y]= lambdas[0]; m_lambda[SAO_Cb]= lambdas[1]; m_lambda[SAO_Cr]= lambdas[2]; TComPicYuv* srcYuv = m_tempPicYuv; resYuv->copyToPic(srcYuv); srcYuv->setBorderExtension(false); srcYuv->extendPicBorder(); //collect statistics // 收集像素块的统计信息,用于判断像素属于EO的那个种类/BO的哪个边带 getStatistics(m_statData, orgYuv, srcYuv, pPic); #if SAO_ENCODE_ALLOW_USE_PREDEBLOCK if(isPreDBFSamplesUsed) { addPreDBFStatistics(m_statData); // 添加去方块滤波的统计信息 } #endif //slice on/off // 判断三个颜色分量是否需要进行SAO decidePicParams(sliceEnabled, pPic->getSlice(0)->getDepth()); //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()); delete[] reconParams; }
相关文章推荐
- HM编码器代码阅读(45)——样点自适应补偿SAO(四)对重建像素进行补偿
- HM编码器代码阅读(43)——样点自适应补偿SAO(二)收集像素块的统计信息
- HM编码器代码阅读(44)——样点自适应补偿SAO(三)选取最优的SAO模式并确定补偿值
- HM编码器代码阅读(7)——整个编码流程以及相关的函数
- homerHEVC代码阅读(27)——CTU初始化函数init_ctu
- leveldb代码阅读(1)——leveldb介绍以及使用
- VBA代码实例---单元格复制以及resize函数介绍
- HM编码器代码阅读(1)——介绍以及相关知识
- HM编码器代码阅读(20)——变换以及量化(三)
- ffmpeg结构体以及函数介绍(二) 分类: ffmpeg-SDL-VLC-Live555 2013-08-22 18:03 451人阅读 评论(0) 收藏
- HM编码器代码阅读(2)——框架以及主要流程
- ffmpeg结构体以及函数介绍(一) 分类: ffmpeg-SDL-VLC-Live555 2013-08-22 18:01 543人阅读 评论(0) 收藏
- SAO样点自适应补偿技术实现代码详解(一)
- [置顶] HM编码器代码阅读(46)——SAO总结
- ffmpeg结构体以及函数介绍(三) 分类: ffmpeg-SDL-VLC-Live555 2013-08-22 18:04 599人阅读 评论(0) 收藏
- DC4C代码阅读(11)——与DAG有关的结构以及函数
- HM编码器代码阅读(18)——变换以及量化(一)
- HM编码器代码阅读(21)——熵编码的概念以及在HEVC中应用
- WinAPI--Win32 系统入口函数介绍
- 向大家介绍一款代码阅读工具——Scitools Understand