variance-based psy adaptive quantization
authorJason Garrett-Glaser <darkshikari@gmail.com>
Sun, 30 Mar 2008 23:58:41 +0000 (17:58 -0600)
committerLoren Merritt <pengvado@akuvian.org>
Sun, 30 Mar 2008 23:58:41 +0000 (17:58 -0600)
new options: --aq-mode --aq-strength
AQ is enabled by default

common/common.c
encoder/analyse.c
encoder/encoder.c
encoder/ratecontrol.c
encoder/ratecontrol.h
x264.c
x264.h

index 44d9113..2d00384 100644 (file)
@@ -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 )
index 5032027..de3cf57 100644 (file)
@@ -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 )
index 3dadb02..7637dc2 100644 (file)
@@ -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 );
 
     {
index 0c8a6d7..bb10f5c 100644 (file)
@@ -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 )
index d4af2c0..2aabda7 100644 (file)
@@ -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 f68755d..37736df 100644 (file)
--- a/x264.c
+++ b/x264.c
@@ -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 },
diff --git a/x264.h b/x264.h
index 70c9eaf..e49f1a4 100644 (file)
--- a/x264.h
+++ b/x264.h
@@ -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;