From: Fiona Glaser Date: Sun, 30 Mar 2008 23:58:41 +0000 (-0600) Subject: variance-based psy adaptive quantization X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b59440f09b7eb7e6f30c1131d56843ee92e3751d;p=libx264 variance-based psy adaptive quantization new options: --aq-mode --aq-strength AQ is enabled by default --- diff --git a/common/common.c b/common/common.c index 44d9113f..2d00384f 100644 --- a/common/common.c +++ b/common/common.c @@ -92,6 +92,8 @@ void x264_param_default( x264_param_t *param ) param->rc.i_qp_step = 4; param->rc.f_ip_factor = 1.4; param->rc.f_pb_factor = 1.3; + param->rc.i_aq_mode = X264_AQ_GLOBAL; + param->rc.f_aq_strength = 1.0; param->rc.b_stat_write = 0; param->rc.psz_stat_out = "x264_2pass.log"; @@ -494,6 +496,10 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value ) p->rc.f_ip_factor = atof(value); OPT2("pbratio", "pb-factor") p->rc.f_pb_factor = atof(value); + OPT("aq-mode") + p->rc.i_aq_mode = atoi(value); + OPT("aq-strength") + p->rc.f_aq_strength = atof(value); OPT("pass") { int i = x264_clip3( atoi(value), 0, 3 ); @@ -883,6 +889,9 @@ char *x264_param2string( x264_param_t *p, int b_res ) s += sprintf( s, " ip_ratio=%.2f", p->rc.f_ip_factor ); if( p->i_bframe ) s += sprintf( s, " pb_ratio=%.2f", p->rc.f_pb_factor ); + s += sprintf( s, " aq=%d", p->rc.i_aq_mode ); + if( p->rc.i_aq_mode ) + s += sprintf( s, ":%.2f", p->rc.f_aq_strength ); if( p->rc.psz_zones ) s += sprintf( s, " zones=%s", p->rc.psz_zones ); else if( p->rc.i_zones ) diff --git a/encoder/analyse.c b/encoder/analyse.c index 50320276..de3cf572 100644 --- a/encoder/analyse.c +++ b/encoder/analyse.c @@ -2061,8 +2061,11 @@ void x264_macroblock_analyse( x264_t *h ) int i_cost = COST_MAX; int i; - /* init analysis */ - x264_mb_analyse_init( h, &analysis, x264_ratecontrol_qp( h ) ); + h->mb.i_qp = x264_ratecontrol_qp( h ); + if( h->param.rc.i_aq_mode ) + x264_adaptive_quant( h ); + + x264_mb_analyse_init( h, &analysis, h->mb.i_qp ); /*--------------------------- Do the analysis ---------------------------*/ if( h->sh.i_type == SLICE_TYPE_I ) diff --git a/encoder/encoder.c b/encoder/encoder.c index 3dadb022..7637dc2b 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -401,6 +401,7 @@ static int x264_validate_parameters( x264_t *h ) h->param.analyse.b_fast_pskip = 0; h->param.analyse.i_noise_reduction = 0; h->param.analyse.i_subpel_refine = x264_clip3( h->param.analyse.i_subpel_refine, 1, 6 ); + h->param.rc.i_aq_mode = 0; } if( h->param.rc.i_rc_method == X264_RC_CQP ) { @@ -475,6 +476,12 @@ static int x264_validate_parameters( x264_t *h ) if( !h->param.b_cabac ) h->param.analyse.i_trellis = 0; h->param.analyse.i_trellis = x264_clip3( h->param.analyse.i_trellis, 0, 2 ); + h->param.rc.i_aq_mode = x264_clip3( h->param.rc.i_aq_mode, 0, 2 ); + if( h->param.rc.f_aq_strength <= 0 ) + h->param.rc.i_aq_mode = 0; + /* VAQ effectively replaces qcomp, so qcomp is raised towards 1 to compensate. */ + if( h->param.rc.i_aq_mode == X264_AQ_GLOBAL ) + h->param.rc.f_qcompress = x264_clip3f(h->param.rc.f_qcompress + h->param.rc.f_aq_strength / 0.7, 0, 1); h->param.analyse.i_noise_reduction = x264_clip3( h->param.analyse.i_noise_reduction, 0, 1<<16 ); { diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index 0c8a6d75..bb10f5c3 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -128,6 +128,10 @@ struct x264_ratecontrol_t int bframes; /* # consecutive B-frames before this P-frame */ int bframe_bits; /* total cost of those frames */ + /* AQ stuff */ + float aq_threshold; + int *ac_energy; + int i_zones; x264_zone_t *zones; x264_zone_t *prev_zone; @@ -169,6 +173,93 @@ static inline double qscale2bits(ratecontrol_entry_t *rce, double qscale) + rce->misc_bits; } +// Find the total AC energy of the block in all planes. +static int ac_energy_mb( x264_t *h, int mb_x, int mb_y, int *satd ) +{ + DECLARE_ALIGNED_16( static uint8_t flat[16] ) = {128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128}; + unsigned int var=0, sad, ssd, i; + for( i=0; i<3; i++ ) + { + int w = i ? 8 : 16; + int stride = h->fenc->i_stride[i]; + int offset = h->mb.b_interlaced + ? w * (mb_x + (mb_y&~1) * stride) + (mb_y&1) * stride + : w * (mb_x + mb_y * stride); + int pix = i ? PIXEL_8x8 : PIXEL_16x16; + stride <<= h->mb.b_interlaced; + sad = h->pixf.sad[pix]( flat, 0, h->fenc->plane[i]+offset, stride ); + ssd = h->pixf.ssd[pix]( flat, 0, h->fenc->plane[i]+offset, stride ); + var += ssd - (sad * sad >> (i?6:8)); + // SATD to represent the block's overall complexity (bit cost) for intra encoding. + // exclude the DC coef, because nothing short of an actual intra prediction will estimate DC cost. + if( var && satd ) + *satd += h->pixf.satd[pix]( flat, 0, h->fenc->plane[i]+offset, stride ) - sad/2; + } + return var; +} + +void x264_autosense_aq( x264_t *h ) +{ + double total = 0; + double n = 0; + int mb_x, mb_y; + // FIXME: Some of the SATDs might be already calculated elsewhere (ratecontrol?). Can we reuse them? + // FIXME: Is chroma SATD necessary? + for( mb_y=0; mb_ysps->i_mb_height; mb_y++ ) + for( mb_x=0; mb_xsps->i_mb_width; mb_x++ ) + { + int energy, satd=0; + energy = ac_energy_mb( h, mb_x, mb_y, &satd ); + h->rc->ac_energy[mb_x + mb_y * h->sps->i_mb_width] = energy; + /* Weight the energy value by the SATD value of the MB. + * This represents the fact that the more complex blocks in a frame should + * be weighted more when calculating the optimal threshold. This also helps + * diminish the negative effect of large numbers of simple blocks in a frame, + * such as in the case of a letterboxed film. */ + if( energy ) + { + x264_cpu_restore(h->param.cpu); + total += logf(energy) * satd; + n += satd; + } + } + x264_cpu_restore(h->param.cpu); + /* Calculate and store the threshold. */ + h->rc->aq_threshold = n ? total/n : 15; +} + +/***************************************************************************** +* x264_adaptive_quant: + * adjust macroblock QP based on variance (AC energy) of the MB. + * high variance = higher QP + * low variance = lower QP + * This generally increases SSIM and lowers PSNR. +*****************************************************************************/ +void x264_adaptive_quant( x264_t *h ) +{ + int qp = h->mb.i_qp; + int energy = h->param.rc.i_aq_mode == X264_AQ_GLOBAL + ? ac_energy_mb( h, h->mb.i_mb_x, h->mb.i_mb_y, NULL ) + : h->rc->ac_energy[h->mb.i_mb_xy]; + if( energy == 0 ) + h->mb.i_qp = h->mb.i_last_qp; + else + { + float result, qp_adj; + x264_cpu_restore(h->param.cpu); + result = energy; + /* Adjust the QP based on the AC energy of the macroblock. */ + qp_adj = 1.5 * (logf(result) - h->rc->aq_threshold); + if( h->param.rc.i_aq_mode == X264_AQ_LOCAL ) + qp_adj = x264_clip3f( qp_adj, -5, 5 ); + h->mb.i_qp = x264_clip3( qp + qp_adj * h->param.rc.f_aq_strength + .5, h->param.rc.i_qp_min, h->param.rc.i_qp_max ); + /* If the QP of this MB is within 1 of the previous MB, code the same QP as the previous MB, + * to lower the bit cost of the qp_delta. */ + if( abs(h->mb.i_qp - h->mb.i_last_qp) == 1 ) + h->mb.i_qp = h->mb.i_last_qp; + } + h->mb.i_chroma_qp = i_chroma_qp_table[x264_clip3( h->mb.i_qp + h->pps->i_chroma_qp_index_offset, 0, 51 )]; +} int x264_ratecontrol_new( x264_t *h ) { @@ -244,7 +335,7 @@ int x264_ratecontrol_new( x264_t *h ) rc->rate_tolerance = 0.01; } - h->mb.b_variable_qp = rc->b_vbv && !rc->b_2pass; + h->mb.b_variable_qp = (rc->b_vbv && !rc->b_2pass) || h->param.rc.i_aq_mode; if( rc->b_abr ) { @@ -458,10 +549,13 @@ int x264_ratecontrol_new( x264_t *h ) x264_free( p ); } - for( i=1; iparam.i_threads; i++ ) + for( i=0; iparam.i_threads; i++ ) { h->thread[i]->rc = rc+i; - rc[i] = rc[0]; + if( i ) + rc[i] = rc[0]; + if( h->param.rc.i_aq_mode == X264_AQ_LOCAL ) + rc[i].ac_energy = x264_malloc( h->mb.i_mb_count * sizeof(int) ); } return 0; @@ -623,6 +717,8 @@ void x264_ratecontrol_delete( x264_t *h ) x264_free( rc->zones[i].param ); x264_free( rc->zones ); } + for( i=0; iparam.i_threads; i++ ) + x264_free( rc[i].ac_energy ); x264_free( rc ); } @@ -729,6 +825,14 @@ void x264_ratecontrol_start( x264_t *h, int i_force_qp ) if( h->sh.i_type != SLICE_TYPE_B ) rc->last_non_b_pict_type = h->sh.i_type; + + /* Adaptive AQ thresholding algorithm. */ + if( h->param.rc.i_aq_mode == X264_AQ_GLOBAL ) + /* Arbitrary value for "center" of the AQ curve. + * Chosen so that any given value of CRF has on average similar bitrate with and without AQ. */ + h->rc->aq_threshold = logf(5000); + else if( h->param.rc.i_aq_mode == X264_AQ_LOCAL ) + x264_autosense_aq(h); } double predict_row_size( x264_t *h, int y, int qp ) diff --git a/encoder/ratecontrol.h b/encoder/ratecontrol.h index d4af2c05..2aabda7e 100644 --- a/encoder/ratecontrol.h +++ b/encoder/ratecontrol.h @@ -34,6 +34,7 @@ void x264_ratecontrol_mb( x264_t *, int bits ); int x264_ratecontrol_qp( x264_t * ); void x264_ratecontrol_end( x264_t *, int bits ); void x264_ratecontrol_summary( x264_t * ); +void x264_adaptive_quant( x264_t * ); #endif diff --git a/x264.c b/x264.c index f68755d9..37736df3 100644 --- a/x264.c +++ b/x264.c @@ -187,6 +187,14 @@ static void Help( x264_param_t *defaults, int b_longhelp ) H0( " --ipratio QP factor between I and P [%.2f]\n", defaults->rc.f_ip_factor ); H0( " --pbratio QP factor between P and B [%.2f]\n", defaults->rc.f_pb_factor ); H1( " --chroma-qp-offset QP difference between chroma and luma [%d]\n", defaults->analyse.i_chroma_qp_offset ); + H0( " --aq-mode How AQ distributes bits [%d]\n" + " - 0: Disabled\n" + " - 1: Avoid moving bits between frames\n" + " - 2: Move bits between frames\n", defaults->rc.i_aq_mode ); + H0( " --aq-strength Reduces blocking and blurring in flat and\n" + " textured areas. [%.1f]\n" + " - 0.5: weak AQ\n" + " - 1.5: strong AQ\n", defaults->rc.f_aq_strength ); H0( "\n" ); H0( " -p, --pass <1|2|3> Enable multipass ratecontrol\n" " - 1: First pass, creates stats file\n" @@ -407,6 +415,8 @@ static int Parse( int argc, char **argv, { "trellis", required_argument, NULL, 't' }, { "no-fast-pskip", no_argument, NULL, 0 }, { "no-dct-decimate", no_argument, NULL, 0 }, + { "aq-strength", required_argument, NULL, 0 }, + { "aq-mode", required_argument, NULL, 0 }, { "deadzone-inter", required_argument, NULL, '0' }, { "deadzone-intra", required_argument, NULL, '0' }, { "level", required_argument, NULL, 0 }, diff --git a/x264.h b/x264.h index 70c9eafa..e49f1a4c 100644 --- a/x264.h +++ b/x264.h @@ -35,7 +35,7 @@ #include -#define X264_BUILD 58 +#define X264_BUILD 59 /* x264_t: * opaque handler for encoder */ @@ -82,6 +82,9 @@ typedef struct x264_t x264_t; #define X264_RC_CQP 0 #define X264_RC_CRF 1 #define X264_RC_ABR 2 +#define X264_AQ_NONE 0 +#define X264_AQ_LOCAL 1 +#define X264_AQ_GLOBAL 2 static const char * const x264_direct_pred_names[] = { "none", "spatial", "temporal", "auto", 0 }; static const char * const x264_motion_est_names[] = { "dia", "hex", "umh", "esa", "tesa", 0 }; @@ -260,6 +263,9 @@ typedef struct x264_param_t float f_ip_factor; float f_pb_factor; + int i_aq_mode; /* psy adaptive QP. (X264_AQ_*) */ + float f_aq_strength; + /* 2pass */ int b_stat_write; /* Enable stat writing in psz_stat_out */ char *psz_stat_out;