Add functions to analyze the noise and correlation of regions.

Change-Id: Iee85953f67ebdc924ce7a362e6bcc5a862ca5067
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c
index 3a6fd3e..de472d9 100644
--- a/av1/encoder/pass2_strategy.c
+++ b/av1/encoder/pass2_strategy.c
@@ -1427,6 +1427,145 @@
   *cur_region_idx = k;
 }
 
+// Estimate the noise variance of each frame from the first pass stats
+static void estimate_region_noise(const FIRSTPASS_STATS *stats,
+                                  const int *is_flash, REGIONS *region) {
+  double C1, C2, C3, noise;
+  int count = 0;
+  region->avg_noise_var = -1;
+  for (int i = region->start + 2; i <= region->last; i++) {
+    if (is_flash[i] || is_flash[i - 1] || is_flash[i - 2]) continue;
+
+    C1 = stats[i - 1].intra_error *
+         (stats[i].intra_error - stats[i].coded_error);
+    C2 = stats[i - 2].intra_error *
+         (stats[i - 1].intra_error - stats[i - 1].coded_error);
+    C3 = stats[i - 2].intra_error *
+         (stats[i].intra_error - stats[i].sr_coded_error);
+    if (C1 <= 0 || C2 <= 0 || C3 <= 0) continue;
+    C1 = sqrt(C1);
+    C2 = sqrt(C2);
+    C3 = sqrt(C3);
+
+    noise = stats[i - 1].intra_error - C1 * C2 / C3;
+    noise = AOMMAX(noise, 0.01);
+    region->avg_noise_var = (region->avg_noise_var == -1)
+                                ? noise
+                                : AOMMIN(noise, region->avg_noise_var);
+    count++;
+  }
+  if (count == 0) {
+    region->avg_noise_var = 0;
+  }
+}
+
+// Analyze the corrrelation coefficient of each frame with its previous frame in
+// a region. Also get the average of stats inside a region.
+// Before calling this function, the region's noise variance is needed.
+static void analyze_region(const FIRSTPASS_STATS *stats, int region_idx,
+                           REGIONS *regions, double *coeff) {
+  double cor_coeff;
+
+  int i, k = region_idx;
+  regions[k].avg_cor_coeff = 0;
+  regions[k].avg_sr_fr_ratio = 0;
+  regions[k].avg_intra_err = 0;
+  regions[k].avg_coded_err = 0;
+
+  int check_first_sr = (k != 0);
+
+  for (i = regions[k].start; i <= regions[k].last; i++) {
+    double C = sqrt(stats[i - 1].intra_error *
+                    (stats[i].intra_error - stats[i].coded_error));
+    cor_coeff = C / (stats[i - 1].intra_error - regions[k].avg_noise_var);
+
+    if (i > regions[k].start || check_first_sr) {
+      double num_frames =
+          (double)(regions[k].last - regions[k].start + check_first_sr);
+      double max_coded_error =
+          AOMMAX(stats[i].coded_error, stats[i - 1].coded_error);
+      double this_ratio = stats[i].sr_coded_error / max_coded_error;
+      regions[k].avg_sr_fr_ratio += this_ratio / num_frames;
+    }
+
+    regions[k].avg_intra_err +=
+        stats[i].intra_error / (double)(regions[k].last - regions[k].start + 1);
+    regions[k].avg_coded_err +=
+        stats[i].coded_error / (double)(regions[k].last - regions[k].start + 1);
+
+    coeff[i] =
+        cor_coeff * sqrt((stats[i - 1].intra_error - regions[k].avg_noise_var) /
+                         (stats[i].intra_error - regions[k].avg_noise_var));
+    // clip correlation coefficient.
+    coeff[i] = AOMMIN(AOMMAX(coeff[i], 0), 1);
+
+    regions[k].avg_cor_coeff +=
+        coeff[i] / (double)(regions[k].last - regions[k].start + 1);
+  }
+}
+
+// Calculate the regions stats of every region. Uses the stable regions to
+// estimate noise variance of other regions. Then call analyze_region for each.
+static void get_region_stats(const FIRSTPASS_STATS *stats, const int *is_flash,
+                             REGIONS *regions, double *coeff, int num_regions) {
+  int k, count_stable = 0;
+  // Analyze stable regions.
+  for (k = 0; k < num_regions; k++) {
+    if (regions[k].type == STABLE_REGION) {
+      estimate_region_noise(stats, is_flash, regions + k);
+      analyze_region(stats, k, regions, coeff);
+      count_stable++;
+    }
+  }
+
+  if (count_stable == 0) {
+    // no stable region, just use the lowest noise variance estimated.
+    double lowest_noise = -1;
+    for (k = 0; k < num_regions; k++) {
+      if (regions[k].type == SCENECUT_REGION) continue;
+      estimate_region_noise(stats, is_flash, regions + k);
+      if (regions[k].avg_noise_var < 0.01) continue;
+      if (lowest_noise < 0 || lowest_noise > regions[k].avg_noise_var) {
+        lowest_noise = regions[k].avg_noise_var;
+      }
+    }
+    lowest_noise = AOMMAX(lowest_noise, 0);
+    for (k = 0; k < num_regions; k++) {
+      regions[k].avg_noise_var = lowest_noise;
+      analyze_region(stats, k, regions, coeff);
+    }
+    return;
+  }
+
+  // Analyze other regions
+  for (k = 0; k < num_regions; k++) {
+    if (regions[k].type != STABLE_REGION &&
+        regions[k].type != SCENECUT_REGION) {
+      // use the average of the nearest previous and next stable regions
+      int count = 0;
+      regions[k].avg_noise_var = 0;
+      for (int r = k - 1; r >= 0; r--) {
+        if (regions[r].type == STABLE_REGION) {
+          count++;
+          regions[k].avg_noise_var += regions[r].avg_noise_var;
+          break;
+        }
+      }
+      for (int r = k + 1; r < num_regions; r++) {
+        if (regions[r].type == STABLE_REGION) {
+          count++;
+          regions[k].avg_noise_var += regions[r].avg_noise_var;
+          break;
+        }
+      }
+      if (count) {
+        regions[k].avg_noise_var /= (double)count;
+      }
+      analyze_region(stats, k, regions, coeff);
+    }
+  }
+}
+
 #endif
 
 /*!\brief Determine the length of future GF groups.