如上一篇Quantization所说,可以在编码端通过设置offset来调整量化后的值,从而趋向于期望的量化值,而且在逆量化公式可以看出offset值在逆量化的时候是不会用到的。
目前来说,确定offset的算法有三种:static offset、around offset、trellis offset。
Static Offset
H.264参考模型建议:当帧内预测时:$f = \frac{1}{3}\bigtriangleup$;当帧间预测时$f = \frac{1}{6}\bigtriangleup$。这种采用固定的比例作为量化的offset。
Around Offset
量化的时候加上Offset,目的是为了通过Offset的调整,使量化能趋向于得到最优结果。那么如何才是最优?当然是对量化后level进行反量化后,得到的数值与量化前的数值保持一致。当然这是不可能的,不过我们可以对第i次的量化结果,反馈到第i+1次量化计算中。通过这种自行反馈的方式,调整量化offset,使其趋向于最优的量化结果。
Around Offset会采用当前位置的上一次量化结果对这次的量化offset进行调整。
$ M_{i+1} = M_i + weight \times ((coeff_i – level_i << Qbits_i) >> (Qbits_i + 1))$
$ f_{i+1} = M_{i+1} << Qbits_{i+1}$
//Q_offsets.c //fi+1 = Mi+1 << Qbitsi+1 static inline void update_q_offset4x4(LevelQuantParams **q_params, short *offsetList, int q_bits) { int i, j; short *p_list = &offsetList[0]; for (j = 0; j < 4; j++) { for (i = 0; i < 4; i++) { q_params[j][i].OffsetComp = (int) *p_list++ << q_bits; } } } /*! ************************************************************************ * \brief * Calculation of the quantization offset parameters at the frame level * * \par Input: * none * * \par Output: * none ************************************************************************ */ void CalculateOffset4x4Param (VideoParameters *p_Vid) { QuantParameters *p_Quant = p_Vid->p_Quant; int k; int qp_per, qp; int img_type = ((p_Vid->type == SI_SLICE) ? I_SLICE : (p_Vid->type == SP_SLICE ? P_SLICE : p_Vid->type)); int max_qp_scale = imax(p_Vid->bitdepth_luma_qp_scale, p_Vid->bitdepth_chroma_qp_scale); int max_qp = 51 + max_qp_scale; InputParameters *p_Inp = p_Vid->p_Inp; p_Vid->AdaptRndWeight = p_Inp->AdaptRndWFactor [p_Vid->nal_reference_idc != 0][img_type]; p_Vid->AdaptRndCrWeight = p_Inp->AdaptRndCrWFactor[p_Vid->nal_reference_idc != 0][img_type]; if (img_type == I_SLICE ) { for (qp = 0; qp < max_qp + 1; qp++) { k = p_Quant->qp_per_matrix [qp]; qp_per = Q_BITS + k - OffsetBits; k = p_Inp->AdaptRoundingFixed ? 0 : qp; // Intra4x4 luma update_q_offset4x4(p_Quant->q_params_4x4[0][1][qp], p_Quant->OffsetList4x4[k][ 0], qp_per); // Intra4x4 chroma u update_q_offset4x4(p_Quant->q_params_4x4[1][1][qp], p_Quant->OffsetList4x4[k][ 1], qp_per); // Intra4x4 chroma v update_q_offset4x4(p_Quant->q_params_4x4[2][1][qp], p_Quant->OffsetList4x4[k][ 2], qp_per); } } else if (img_type == B_SLICE) { for (qp = 0; qp < max_qp + 1; qp++) { k = p_Quant->qp_per_matrix [qp]; qp_per = Q_BITS + k - OffsetBits; k = p_Inp->AdaptRoundingFixed ? 0 : qp; // Inter4x4 luma update_q_offset4x4(p_Quant->q_params_4x4[0][0][qp], p_Quant->OffsetList4x4[k][12], qp_per); // Intra4x4 luma update_q_offset4x4(p_Quant->q_params_4x4[0][1][qp], p_Quant->OffsetList4x4[k][ 6], qp_per); // Inter4x4 chroma u update_q_offset4x4(p_Quant->q_params_4x4[1][0][qp], p_Quant->OffsetList4x4[k][13], qp_per); // Intra4x4 chroma u update_q_offset4x4(p_Quant->q_params_4x4[1][1][qp], p_Quant->OffsetList4x4[k][ 7], qp_per); // Inter4x4 chroma v update_q_offset4x4(p_Quant->q_params_4x4[2][0][qp], p_Quant->OffsetList4x4[k][14], qp_per); // Intra4x4 chroma v update_q_offset4x4(p_Quant->q_params_4x4[2][1][qp], p_Quant->OffsetList4x4[k][ 8], qp_per); } } else { for (qp = 0; qp < max_qp + 1; qp++) { k = p_Quant->qp_per_matrix [qp]; qp_per = Q_BITS + k - OffsetBits; k = p_Inp->AdaptRoundingFixed ? 0 : qp; // Inter4x4 luma update_q_offset4x4(p_Quant->q_params_4x4[0][0][qp], p_Quant->OffsetList4x4[k][ 9], qp_per); // Intra4x4 luma update_q_offset4x4(p_Quant->q_params_4x4[0][1][qp], p_Quant->OffsetList4x4[k][ 3], qp_per); // Inter4x4 chroma u update_q_offset4x4(p_Quant->q_params_4x4[1][0][qp], p_Quant->OffsetList4x4[k][10], qp_per); // Intra4x4 chroma u update_q_offset4x4(p_Quant->q_params_4x4[1][1][qp], p_Quant->OffsetList4x4[k][ 4], qp_per); // Inter4x4 chroma v update_q_offset4x4(p_Quant->q_params_4x4[2][0][qp], p_Quant->OffsetList4x4[k][11], qp_per); // Intra4x4 chroma v update_q_offset4x4(p_Quant->q_params_4x4[2][1][qp], p_Quant->OffsetList4x4[k][ 5], qp_per); } } } //Q_around.c //Mi+1 = Mi + k /*! ************************************************************************ * \brief * update rounding offsets based on JVT-N011 ************************************************************************ */ void update_offset_params(Macroblock *currMB, int mode, byte luma_transform_size_8x8_flag) { VideoParameters *p_Vid = currMB->p_Vid; InputParameters *p_Inp = currMB->p_Inp; int is_inter = (mode != I4MB)&&(mode != I16MB) && (mode != I8MB); int luma_pos = AdaptRndPos[(is_inter<<1) + luma_transform_size_8x8_flag][p_Vid->type]; int i,j; int qp = currMB->qp + p_Vid->bitdepth_luma_qp_scale; int cur_qp = p_Inp->AdaptRoundingFixed ? 0 : qp; int temp = 0; QuantParameters *p_Quant = p_Vid->p_Quant; int offsetRange = 1 << (OffsetBits - 1); int blk_mask = 0x03 + (luma_transform_size_8x8_flag<<2); int blk_shift = 2 + luma_transform_size_8x8_flag; short **offsetList = luma_transform_size_8x8_flag ? p_Quant->OffsetList8x8[cur_qp] : p_Quant->OffsetList4x4[cur_qp]; short *cur_offset_list = offsetList[luma_pos]; int **fAdjust = luma_transform_size_8x8_flag ? p_Vid->ARCofAdj8x8[0][mode] : p_Vid->ARCofAdj4x4[0][mode]; if (mode == IPCM) return; if( (p_Vid->active_sps->chroma_format_idc == YUV444) && (p_Inp->separate_colour_plane_flag != 0) ) { if( luma_transform_size_8x8_flag ) // 8x8 luma_pos += 5 * p_Vid->colour_plane_id; else // 4x4 luma_pos += p_Vid->colour_plane_id; cur_offset_list = offsetList[luma_pos]; } for (j=0; j < MB_BLOCK_SIZE; j++) { int j_pos = ((j & blk_mask)<<blk_shift); for (i=0; i < MB_BLOCK_SIZE; i++) { temp = j_pos + (i & blk_mask); cur_offset_list[temp] = (short) iClip3(0, offsetRange, cur_offset_list[temp] + (short) fAdjust[j][i]); } } if(p_Vid->P444_joined) { int **fAdjustCbCr; int uv; for(uv = 0; uv < 2; uv++) { luma_pos = AdaptRndPos[(is_inter<<1) + luma_transform_size_8x8_flag][p_Vid->type]; fAdjustCbCr = luma_transform_size_8x8_flag ? p_Vid->ARCofAdj8x8[uv + 1][mode] : p_Vid->ARCofAdj4x4[uv + 1][mode]; if(luma_transform_size_8x8_flag ) // 8x8 luma_pos += 5 * (uv+1); else // 4x4 luma_pos += (uv+1); cur_offset_list = offsetList[luma_pos]; for (j=0; j < MB_BLOCK_SIZE; j++) { int j_pos = ((j & blk_mask)<<blk_shift); for (i=0; i < MB_BLOCK_SIZE; i++) { temp = j_pos + (i & blk_mask); cur_offset_list[temp] = (short) iClip3(0, offsetRange, cur_offset_list[temp] + (short) fAdjustCbCr[j][i]); } } } } if ((p_Inp->AdaptRndChroma) && (p_Vid->yuv_format == YUV420 || p_Vid->yuv_format == YUV422 )) { int u_pos = AdaptRndCrPos[is_inter][p_Vid->type]; int v_pos = u_pos + 1; int k, jpos, uv = 1; for (k = u_pos; k <= v_pos; k++) { int **fAdjustChroma = (luma_transform_size_8x8_flag && mode == P8x8 )? p_Vid->ARCofAdj4x4[uv][4] : p_Vid->ARCofAdj4x4[uv][mode]; uv++; cur_offset_list = p_Quant->OffsetList4x4[cur_qp][k]; for (j = 0; j < p_Vid->mb_cr_size_y; j++) { jpos = ((j & 0x03)<<2); for (i = 0; i < p_Vid->mb_cr_size_x; i++) { temp = jpos + (i & 0x03); cur_offset_list[temp] = (short) iClip3(0, offsetRange, cur_offset_list[temp] + (short) fAdjustChroma[j][i]); } } } } } //Quant4x4_around.c //k = weight * ((coeff - level<<Qbits) >> Qbits+1) /*! ************************************************************************ * \brief * Quantization process for All coefficients for a 4x4 block * ************************************************************************ */ int quant_4x4_around(Macroblock *currMB, int **tblock, struct quant_methods *q_method) { VideoParameters *p_Vid = currMB->p_Vid; QuantParameters *p_Quant = p_Vid->p_Quant; Slice *currSlice = currMB->p_Slice; Boolean is_cavlc = (Boolean) (currSlice->symbol_mode == CAVLC); int AdaptRndWeight = p_Vid->AdaptRndWeight; int block_x = q_method->block_x; int qp = q_method->qp; int* ACL = &q_method->ACLevel[0]; int* ACR = &q_method->ACRun[0]; LevelQuantParams **q_params_4x4 = q_method->q_params; const byte (*pos_scan)[2] = q_method->pos_scan; const byte *c_cost = q_method->c_cost; int *coeff_cost = q_method->coeff_cost; LevelQuantParams *q_params = NULL; int **fadjust4x4 = q_method->fadjust; int i,j, coeff_ctr; int *m7; int scaled_coeff; int level, run = 0; int nonzero = FALSE; int qp_per = p_Quant->qp_per_matrix[qp]; int q_bits = Q_BITS + qp_per; const byte *p_scan = &pos_scan[0][0]; int* padjust4x4; // Quantization for (coeff_ctr = 0; coeff_ctr < 16; ++coeff_ctr) { i = *p_scan++; // horizontal position j = *p_scan++; // vertical position padjust4x4 = &fadjust4x4[j][block_x + i]; m7 = &tblock[j][block_x + i]; if (*m7 != 0) { q_params = &q_params_4x4[j][i]; scaled_coeff = iabs (*m7) * q_params->ScaleComp; level = (scaled_coeff + q_params->OffsetComp) >> q_bits; if (level != 0) { if (is_cavlc) level = imin(level, CAVLC_LEVEL_LIMIT); *padjust4x4 = rshift_rnd_sf((AdaptRndWeight * (scaled_coeff - (level << q_bits))), q_bits + 1); *coeff_cost += (level > 1) ? MAX_VALUE : c_cost[run]; level = isignab(level, *m7); *m7 = rshift_rnd_sf(((level * q_params->InvScaleComp) << qp_per), 4); // inverse scale can be alternative performed as follows to ensure 16bit // arithmetic is satisfied. // *m7 = (qp_per<4) ? rshift_rnd_sf((level*q_params->InvScaleComp),4-qp_per) : (level*q_params->InvScaleComp)<<(qp_per-4); *ACL++ = level; *ACR++ = run; // reset zero level counter run = 0; nonzero = TRUE; } else { *padjust4x4 = 0; *m7 = 0; ++run; } } else { *padjust4x4 = 0; ++run; } } *ACL = 0; return nonzero; }