Commit b59440f0 authored by Fiona Glaser's avatar Fiona Glaser Committed by Loren Merritt

variance-based psy adaptive quantization

new options: --aq-mode --aq-strength
AQ is enabled by default
parent 8d8f3ea4
......@@ -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 )
......
......@@ -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 )
......
......@@ -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 );
{
......
......@@ -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_y<h->sps->i_mb_height; mb_y++ )
for( mb_x=0; mb_x<h->sps->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; i<h->param.i_threads; i++ )
for( i=0; i<h->param.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; i<h->param.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 )
......
......@@ -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
......@@ -187,6 +187,14 @@ static void Help( x264_param_t *defaults, int b_longhelp )
H0( " --ipratio <float> QP factor between I and P [%.2f]\n", defaults->rc.f_ip_factor );
H0( " --pbratio <float> QP factor between P and B [%.2f]\n", defaults->rc.f_pb_factor );
H1( " --chroma-qp-offset <integer> QP difference between chroma and luma [%d]\n", defaults->analyse.i_chroma_qp_offset );
H0( " --aq-mode <integer> 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 <float> 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 },
......
......@@ -35,7 +35,7 @@
#include <stdarg.h>
#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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment