Add a SB AQ mode for all intra frame perceptual quality

Allow the codec to offset QPs on superblock basis, based on the
relative variance metric after Wiener filter.

Change-Id: I2838a0c87a841c542742727b4899ca58514cabe1
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index b998c6d..136a5db 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -248,6 +248,7 @@
   // Delta-q modulation based on variance
   av1_setup_src_planes(x, cpi->source, mi_row, mi_col, num_planes, sb_size);
 
+  const int delta_q_res = delta_q_info->delta_q_res;
   int current_qindex = cm->quant_params.base_qindex;
   if (cpi->oxcf.q_cfg.deltaq_mode == DELTA_Q_PERCEPTUAL) {
     if (DELTA_Q_PERCEPTUAL_MODULATION == 1) {
@@ -267,9 +268,12 @@
     // Setup deltaq based on tpl stats
     current_qindex =
         av1_get_q_for_deltaq_objective(cpi, sb_size, mi_row, mi_col);
+  } else if (cpi->oxcf.q_cfg.deltaq_mode == DELTA_Q_PERCEPTUAL_AI) {
+    current_qindex = av1_get_sbq_perceptual_ai(cpi, sb_size, mi_row, mi_col);
+    current_qindex =
+        clamp(current_qindex, delta_q_res, 256 - delta_q_info->delta_q_res);
   }
 
-  const int delta_q_res = delta_q_info->delta_q_res;
   // Right now deltaq only works with tpl model. So if tpl is disabled, we set
   // the current_qindex to base_qindex.
   if (cpi->oxcf.algo_cfg.enable_tpl_model &&
@@ -1395,6 +1399,8 @@
       cm->delta_q_info.delta_q_res = DEFAULT_DELTA_Q_RES_OBJECTIVE;
     else if (deltaq_mode == DELTA_Q_PERCEPTUAL)
       cm->delta_q_info.delta_q_res = DEFAULT_DELTA_Q_RES_PERCEPTUAL;
+    else if (deltaq_mode == DELTA_Q_PERCEPTUAL_AI)
+      cm->delta_q_info.delta_q_res = DEFAULT_DELTA_Q_RES_PERCEPTUAL;
     // Set delta_q_present_flag before it is used for the first time
     cm->delta_q_info.delta_lf_res = DEFAULT_DELTA_LF_RES;
     cm->delta_q_info.delta_q_present_flag = deltaq_mode != NO_DELTA_Q;
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index e18c943..ce9fdcc 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -3212,6 +3212,49 @@
   cpi->norm_wiener_variance = AOMMAX(1, cpi->norm_wiener_variance);
 }
 
+int av1_get_sbq_perceptual_ai(AV1_COMP *const cpi, BLOCK_SIZE bsize, int mi_row,
+                              int mi_col) {
+  AV1_COMMON *const cm = &cpi->common;
+  const int mi_wide = mi_size_wide[bsize];
+  const int mi_high = mi_size_high[bsize];
+  const int base_qindex = cm->quant_params.base_qindex;
+
+  const int mi_step = mi_size_wide[BLOCK_16X16];
+  int64_t sb_wiener_var;
+  int mb_stride = cpi->frame_info.mb_cols;
+  int mb_count = 0;
+  int mb_wiener_var[64];
+
+  for (int row = mi_row; row < mi_row + mi_high; row += mi_step) {
+    for (int col = mi_col; col < mi_col + mi_wide; col += mi_step) {
+      if (row >= cm->mi_params.mi_rows || col >= cm->mi_params.mi_cols)
+        continue;
+
+      mb_wiener_var[mb_count] =
+          (int)cpi->mb_wiener_variance[(row / mi_step) * mb_stride +
+                                       (col / mi_step)];
+      ++mb_count;
+    }
+  }
+  qsort(mb_wiener_var, mb_count - 1, sizeof(*mb_wiener_var), qsort_comp);
+
+  sb_wiener_var = mb_wiener_var[mb_count / 2];
+  sb_wiener_var = AOMMAX(1, sb_wiener_var);
+
+  int offset = 0;
+  double beta = (double)cpi->norm_wiener_variance / sb_wiener_var;
+  offset = av1_get_deltaq_offset(cm->seq_params->bit_depth, base_qindex, beta);
+  const DeltaQInfo *const delta_q_info = &cm->delta_q_info;
+  offset = AOMMIN(offset, delta_q_info->delta_q_res * 9 - 1);
+  offset = AOMMAX(offset, -delta_q_info->delta_q_res * 9 + 1);
+  int qindex = cm->quant_params.base_qindex + offset;
+  qindex = AOMMIN(qindex, MAXQ);
+  qindex = AOMMAX(qindex, MINQ);
+  if (base_qindex > MINQ) qindex = AOMMAX(qindex, MINQ + 1);
+
+  return qindex;
+}
+
 extern void av1_print_frame_contexts(const FRAME_CONTEXT *fc,
                                      const char *filename);
 
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index d694753..fe9d76e 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -3159,6 +3159,9 @@
 
 void av1_update_frame_size(AV1_COMP *cpi);
 
+int av1_get_sbq_perceptual_ai(AV1_COMP *const cpi, BLOCK_SIZE bsize, int mi_row,
+                              int mi_col);
+
 #if CONFIG_FRAME_PARALLEL_ENCODE
 typedef struct {
   int pyr_level;