homerHEVC代码阅读(36)——环路滤波(2)SAO

来源:互联网 发布:图像尺寸测量软件 编辑:程序博客网 时间:2024/06/05 22:32

一、环路滤波的理论知识请看 HEVC/H.265理论知识(7)——环路滤波 

二、SAO介绍
    1、振铃效应是指在块的边缘范围产生波纹现象,会影响视频的主观质量
    2、根本原因就是高频信息的丢失(在变化和量化中丢失)
    3、SAO就是对波纹曲线进行操作,在波峰的地方添加负值、在波谷的地方添加正值、以达到去除振铃效应的目的
    4、SAO以CTU为处理单位
    5、SAO又可以分成三大类:边界补偿EO、边带补偿BO、参数融合技术(merge)


三、SAO技术
    1、边界补偿EO:
     (1)先通过和相邻像素值进行比较,对当前的像素进行归类,比较的时候有四种比较模式。
     (2)有四种模式:EO_0(水平方向)、EO_1(垂直方向)、EO_2(135度方向(即从左上角到右下角))、EO_3(45度方向)
     (3)当前的像素可以被归类成五个类型之一:0(无意义)、1(波谷状)、2(凹角)、3(凸角)、4(波峰状)
     (4)同一类型的像素值采用相同的补偿值
    2、边带补偿BO:
     (1)对当前的像素进行分类,可以分成32类,每一类称为边带
     (2)每一个边带才用相同的补偿值
    3、参数融合:
     (1)对于一个CTU,它的SAO参数直接使用相邻块的SAO参数(上面的或者左边的)
     (2)要么选择left块的SAO参数
     (3)要么选择up块的SAO参数
     (4)要么分析自己CTU块像素的特点,自己决定SAO参数


SAO的入口函数hmr_wpp_sao_ctu,它的作用主要是进行一些初始化,然后调用sao_decide_blk_params,下面直接讲解sao_decide_blk_params函数:

1、SAO的merge模式的候选SAO参数的列表

2、遍历HEVC支持的所有SAO模式,目的是为了选取最优SAO模式

3、根据选取的SAO参数调用reconstruct_blk_sao_param,对像素块进行滤波!

void sao_decide_blk_params(henc_thread_t *wpp_thread, slice_t *currslice, ctu_info_t *ctu, sao_stat_data_t stats[][NUM_SAO_NEW_TYPES], int *slice_enable){sao_blk_param_t* merge_list[12];int is_all_blks_disabled = FALSE;sao_blk_param_t mode_param;double minCost, modeCost;int ctu_idx = ctu->ctu_number;int component;int num_lcus_for_sao_off[NUM_PICT_COMPONENTS];sao_blk_param_t *coded_params = &ctu->coded_params;sao_blk_param_t *recon_params = &ctu->recon_params;if (!slice_enable[Y_COMP] && !slice_enable[U_COMP] && !slice_enable[V_COMP]){is_all_blks_disabled = TRUE;}//for(ctu_idx=0; ctu_idx< enc_engine->pict_total_ctu; ctu_idx++){int mode;int merge_list_size;// 如果禁用了所有块的SAO,那么直接返回if (is_all_blks_disabled){sao_blk_param_t *cp = coded_params;for (component = 0; component < 3; component++){cp->offsetParam[component].modeIdc = SAO_MODE_OFF;cp->offsetParam[component].typeIdc = -1;cp->offsetParam[component].typeAuxInfo = -1;memset(cp->offsetParam[component].offset, 0, sizeof(cp->offsetParam[component].offset));}return;}// 得到SAO的merge模式的SAO参数候选列表sao_get_merge_list(wpp_thread, ctu, merge_list, &merge_list_size);minCost = MAX_COST;// 遍历HEVC支持的所有SAO模式(为了选出最优的模式)for (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;// 普通的SAO模式:EO、BO等case SAO_MODE_NEW:{sao_derive_mode_new_rdo(wpp_thread, merge_list, merge_list_size, stats, &mode_param, &modeCost, slice_enable);}break;// merge模式case SAO_MODE_MERGE:{sao_derive_mode_merge_rdo(wpp_thread, merge_list, merge_list_size, slice_enable, stats, &mode_param, &modeCost);//, TEncSbac** cabacCoderRDO, Int inCabacLabel)}break;default:{printf("Not a supported SAO mode\n");exit(-1);}}if (modeCost < minCost){minCost = modeCost;*coded_params = mode_param;}} //modewpp_thread->enc_engine->sao_debug_mode[coded_params->offsetParam[Y_COMP].modeIdc]++;wpp_thread->enc_engine->sao_debug_mode[coded_params->offsetParam[U_COMP].modeIdc]++;wpp_thread->enc_engine->sao_debug_mode[coded_params->offsetParam[V_COMP].modeIdc]++;*recon_params = *coded_params;reconstruct_blk_sao_param(recon_params, merge_list, merge_list_size);} //ctu}


返回SAO的merge模式的候选参数列表:

int sao_get_merge_list(henc_thread_t *wpp_thread, ctu_info_t *ctu, sao_blk_param_t* merge_list[], int *merge_list_size){int ctu_idx = ctu->ctu_number;int ctuX = ctu_idx % wpp_thread->pict_width_in_ctu;int ctuY = ctu_idx / wpp_thread->pict_width_in_ctu;int mergedCTUPos;int numValidMergeCandidates = 0;int merge_type;int merge_idx = 0;for (merge_type = 0; merge_type < NUM_SAO_MERGE_TYPES; merge_type++){sao_blk_param_t *mergeCandidate = NULL;switch (merge_type){/* 和上面合并 */case SAO_MERGE_ABOVE:{if (ctuY > 0){ctu_info_t *ctu_aux = ctu - wpp_thread->pict_width_in_ctu;{mergeCandidate = &(ctu_aux->recon_params);}}}break;/* 和左边合并 */case SAO_MERGE_LEFT:{if (ctuX > 0){ctu_info_t *ctu_aux = ctu - 1;{mergeCandidate = &(ctu_aux->recon_params);}}}break;default:{printf("not a supported merge type");////assert(0);exit(-1);}}merge_list[merge_idx++] = mergeCandidate;if (mergeCandidate != NULL){numValidMergeCandidates++;}}*merge_list_size = merge_idx;return numValidMergeCandidates;}

SAO的merge模式的处理,主要的就是一个计算率失真代价的过程:

void sao_derive_mode_merge_rdo(henc_thread_t *wpp_thread, sao_blk_param_t** merge_list, int merge_list_size, int* slice_enable, sao_stat_data_t stats[][NUM_SAO_NEW_TYPES], sao_blk_param_t* mode_param, double *mode_cost)//, TEncSbac** cabacCoderRDO, Int inCabacLabel){double cost;sao_blk_param_t test_blk_param;int merge_type;int component;uint rate;double *lambdas = wpp_thread->enc_engine->sao_lambdas;//  int mergeListSize = (Int)mergeList.size();*mode_cost = MAX_COST;for (merge_type = 0; merge_type < merge_list_size; merge_type++){double norm_dist = 0;if (merge_list[merge_type] == NULL){continue;}test_blk_param = *(merge_list[merge_type]);//normalized distortionfor (component = 0; component < NUM_PICT_COMPONENTS; component++){sao_offset_t *merged_offsetparam;test_blk_param.offsetParam[component].modeIdc = SAO_MODE_MERGE;test_blk_param.offsetParam[component].typeIdc = merge_type;merged_offsetparam = &(*(merge_list[merge_type])).offsetParam[component];if (merged_offsetparam->modeIdc != SAO_MODE_OFF){norm_dist += (((double)sao_get_distortion(merged_offsetparam->typeIdc, merged_offsetparam->typeAuxInfo, merged_offsetparam->offset, &stats[component][merged_offsetparam->typeIdc], wpp_thread->bit_depth))) / wpp_thread->enc_engine->sao_lambdas[component];///m_lambda[component]);}}// 计算率失真rate = rd_code_sao_blk_param(wpp_thread, &test_blk_param, slice_enable, (merge_list[SAO_MERGE_LEFT] != NULL), (merge_list[SAO_MERGE_ABOVE] != NULL), FALSE, wpp_thread->ee->contexts, wpp_thread->ee->b_ctx);cost = norm_dist;#ifndef COMPUTE_AS_HMcost += (double)rate;#endifif (cost < *mode_cost){*mode_cost = cost;*mode_param = test_blk_param;}}}
SAO的普通模式(即EO、BO)的处理,同样也是计算率失真,然后选择最优模式:

void sao_derive_mode_new_rdo(henc_thread_t *wpp_thread, sao_blk_param_t** merge_list, int merge_list_size, sao_stat_data_t stats[][NUM_SAO_NEW_TYPES], sao_blk_param_t *mode_param, double *mode_cost, int slice_enabled[]){double minCost, cost;int rate;uint previousWrittenBits;int64_t dist[NUM_PICT_COMPONENTS], modeDist[NUM_PICT_COMPONENTS];sao_offset_t testOffset[NUM_PICT_COMPONENTS];int component;int invQuantOffset[MAX_NUM_SAO_CLASSES];int type_idc;double *lambdas = wpp_thread->enc_engine->sao_lambdas;modeDist[Y_COMP] = modeDist[U_COMP] = modeDist[V_COMP] = 0;//------ luma --------//component = Y_COMP;//"off" case as initial costmode_param->offsetParam[component].modeIdc = SAO_MODE_OFF;modeDist[component] = 0;#ifdef COMPUTE_AS_HM//minCost = MAX_COST;minCost = 2.5*lambdas[component];//MAX_COST;#elserate = rd_code_sao_offset_param(wpp_thread, component, &mode_param->offsetParam[component], slice_enabled[component], wpp_thread->ee->contexts, wpp_thread->ee->b_ctx);minCost = lambdas[component] * rate;bm_copy_binary_model(wpp_thread->ec->b_ctx, &wpp_thread->aux_bm);ee_copy_entropy_model(wpp_thread->ec->contexts, wpp_thread->aux_contexts);#endif// 判断亮度分量是否允许SAO处理if (slice_enabled[component]){// 遍历所有的SAO类型for (type_idc = 0; type_idc < NUM_SAO_NEW_TYPES; type_idc++){testOffset[component].modeIdc = SAO_MODE_NEW;testOffset[component].typeIdc = type_idc;// 计算补偿值sao_derive_offsets(wpp_thread, component, type_idc, &stats[component][type_idc], testOffset[component].offset, &testOffset[component].typeAuxInfo);sao_invert_quant_offsets(component, type_idc, testOffset[component].typeAuxInfo, invQuantOffset, testOffset[component].offset);// 计算失真dist[component] = sao_get_distortion(testOffset[component].typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, &stats[component][type_idc], wpp_thread->enc_engine->bit_depth);cost = (double)dist[component];// + m_lambda[component]*((Double)rate);#ifdef COMPUTE_AS_HMif(type_idc==SAO_TYPE_BO)cost += lambdas[component]*11;elsecost += lambdas[component]*8;#else// 计算码率rate = rd_code_sao_offset_param(wpp_thread, component, &testOffset[component], slice_enabled[component], wpp_thread->ee->contexts, wpp_thread->ee->b_ctx);// 代价cost += lambdas[component] * rate;#endif// 更新最优代价if (cost < minCost){minCost = cost;modeDist[component] = dist[component];mode_param->offsetParam[component] = testOffset[component];bm_copy_binary_model(wpp_thread->ec->b_ctx, &wpp_thread->aux_bm);ee_copy_entropy_model(wpp_thread->ec->contexts, wpp_thread->aux_contexts);}}}//------ chroma --------//cost = 0;previousWrittenBits = 0;// 对两个色度分量,计算率失真代价for (component = U_COMP; component < NUM_PICT_COMPONENTS; component++){mode_param->offsetParam[component].modeIdc = SAO_MODE_OFF;modeDist[component] = 0;if (component == U_COMP)rate = rd_code_sao_offset_param(wpp_thread, component, &mode_param->offsetParam[component], slice_enabled[component], wpp_thread->aux_contexts, &wpp_thread->aux_bm);elserate = rd_code_sao_offset_param(wpp_thread, component, &mode_param->offsetParam[component], slice_enabled[component], wpp_thread->ec->contexts, wpp_thread->ec->b_ctx);cost += lambdas[component] * rate;}minCost = cost;//minCost = MAX_COST;#ifdef COMPUTE_AS_HMminCost = 2.5*lambdas[U_COMP];//MAX_COST;#endif// 遍历所有的SAO类型for (type_idc = 0; type_idc < NUM_SAO_NEW_TYPES; type_idc++){previousWrittenBits = 0;cost = 0;// 处理两个色度分量SAOfor (component = U_COMP; component < NUM_PICT_COMPONENTS; component++){if (!slice_enabled[component]){testOffset[component].modeIdc = SAO_MODE_OFF;dist[component] = 0;continue;}testOffset[component].modeIdc = SAO_MODE_NEW;testOffset[component].typeIdc = type_idc;sao_derive_offsets(wpp_thread, component, type_idc, &stats[component][type_idc], testOffset[component].offset, &testOffset[component].typeAuxInfo);sao_invert_quant_offsets(component, type_idc, testOffset[component].typeAuxInfo, invQuantOffset, testOffset[component].offset);dist[component] = sao_get_distortion(testOffset[component].typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, &stats[component][type_idc], wpp_thread->bit_depth);cost += dist[component];// + (m_lambda[component] * (currentWrittenBits - previousWrittenBits));if (component == U_COMP)rate = rd_code_sao_offset_param(wpp_thread, component, &testOffset[component], slice_enabled[component], wpp_thread->aux_contexts, &wpp_thread->aux_bm);elserate = rd_code_sao_offset_param(wpp_thread, component, &testOffset[component], slice_enabled[component], wpp_thread->ec->contexts, wpp_thread->ec->b_ctx);#ifdef COMPUTE_AS_HMif(type_idc==SAO_TYPE_BO)cost += lambdas[component]*11;elsecost += lambdas[component]*8;#elsecost += lambdas[component] * rate;#endif}// 更新最优代价if (cost < minCost){minCost = cost;for (component = U_COMP; component < NUM_PICT_COMPONENTS; component++){modeDist[component] = dist[component];mode_param->offsetParam[component] = testOffset[component];}}}//----- re-gen rate & normalized cost----//*mode_cost = (double)modeDist[Y_COMP] / lambdas[Y_COMP] + (double)modeDist[U_COMP] / lambdas[U_COMP] + (double)modeDist[V_COMP] / lambdas[V_COMP];#ifndef COMPUTE_AS_HM*mode_cost += rd_code_sao_blk_param(wpp_thread, mode_param, slice_enabled, (merge_list[SAO_MERGE_LEFT] != NULL), (merge_list[SAO_MERGE_ABOVE] != NULL), FALSE, wpp_thread->ee->contexts, wpp_thread->ee->b_ctx);#endif}





0 0
原创粉丝点击