Add functions to determine stable and unstable regions.

Change-Id: Ib63a9b9a34a7903c02aee4a978495eed54468d4e
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c
index de472d9..44ae938 100644
--- a/av1/encoder/pass2_strategy.c
+++ b/av1/encoder/pass2_strategy.c
@@ -1566,6 +1566,211 @@
   }
 }
 
+// Find tentative stable regions
+static int find_stable_regions(const FIRSTPASS_STATS *stats,
+                               const double *grad_coded, int this_start,
+                               int this_last, REGIONS *regions) {
+  int i, j, k = 0;
+  regions[k].start = this_start;
+  for (i = this_start; i <= this_last; i++) {
+    // Check mean and variance of stats in a window
+    double mean_intra = 0.001, var_intra = 0.001;
+    double mean_coded = 0.001, var_coded = 0.001;
+    for (j = -HALF_WIN; j <= HALF_WIN; j++) {
+      int idx = AOMMIN(AOMMAX(i + j, this_start), this_last);
+      mean_intra += stats[idx].intra_error / WINDOW_SIZE;
+      var_intra +=
+          stats[idx].intra_error * stats[idx].intra_error / WINDOW_SIZE;
+      mean_coded += stats[idx].coded_error / WINDOW_SIZE;
+      var_coded +=
+          stats[idx].coded_error * stats[idx].coded_error / WINDOW_SIZE;
+    }
+
+    int is_intra_stable = (var_intra / (mean_intra * mean_intra) < 1.03);
+    int is_coded_stable = (var_coded / (mean_coded * mean_coded) < 1.04 &&
+                           fabs(grad_coded[i]) / mean_coded < 0.05) ||
+                          mean_coded / mean_intra < 0.05;
+    int is_coded_small = mean_coded < 0.5 * mean_intra;
+    REGION_TYPES cur_type =
+        (is_intra_stable && is_coded_stable && is_coded_small)
+            ? STABLE_REGION
+            : HIGH_VAR_REGION;
+
+    // mark a new region if type changes
+    if (i == regions[k].start) {
+      // first frame in the region
+      regions[k].type = cur_type;
+    } else if (cur_type != regions[k].type) {
+      // Append a new region
+      regions[k].last = i - 1;
+      regions[k + 1].start = i;
+      regions[k + 1].type = cur_type;
+      k++;
+    }
+  }
+  regions[k].last = this_last;
+  return k + 1;
+}
+
+// Clean up regions that should be removed or merged.
+static void cleanup_regions(REGIONS *regions, int *num_regions) {
+  int k = 0;
+  while (k < *num_regions) {
+    if ((k > 0 && regions[k - 1].type == regions[k].type) ||
+        regions[k].last < regions[k].start) {
+      remove_region(0, regions, num_regions, &k);
+    } else {
+      k++;
+    }
+  }
+}
+
+// Remove regions that are of type and shorter than length.
+// Merge it with its neighboring regions.
+static void remove_short_regions(REGIONS *regions, int *num_regions,
+                                 REGION_TYPES type, int length) {
+  int k = 0;
+  while (k < *num_regions && (*num_regions) > 1) {
+    if ((regions[k].last - regions[k].start + 1 < length &&
+         regions[k].type == type)) {
+      // merge current region with the previous and next regions
+      remove_region(2, regions, num_regions, &k);
+    } else {
+      k++;
+    }
+  }
+  cleanup_regions(regions, num_regions);
+}
+
+static void adjust_unstable_region_bounds(const FIRSTPASS_STATS *stats,
+                                          const int *is_flash,
+                                          const double *grad, REGIONS *regions,
+                                          double *coeff, int *num_regions) {
+  int i, j, k;
+  // Remove regions that are too short. Likely noise.
+  remove_short_regions(regions, num_regions, STABLE_REGION, HALF_WIN);
+  remove_short_regions(regions, num_regions, HIGH_VAR_REGION, HALF_WIN);
+
+  get_region_stats(stats, is_flash, regions, coeff, *num_regions);
+
+  // Adjust region boundaries. The thresholds are empirically obtained, but
+  // overall the performance is not very sensitive to small changes to them.
+  for (k = 0; k < *num_regions; k++) {
+    if (regions[k].type == STABLE_REGION) continue;
+    if (k > 0) {
+      // Adjust previous boundary.
+      // First find the average intra/coded error in the previous
+      // neighborhood.
+      double avg_intra_err = 0, avg_coded_err = 0, avg_coeff = 0;
+      int starti = AOMMAX(regions[k - 1].last - WINDOW_SIZE + 1,
+                          regions[k - 1].start + 1);
+      int lasti = regions[k - 1].last;
+      int counti = 0;
+      for (i = starti; i <= lasti; i++) {
+        avg_intra_err += stats[i].intra_error;
+        avg_coded_err += stats[i].coded_error;
+        avg_coeff += coeff[i];
+        counti++;
+      }
+      if (counti > 0) {
+        avg_intra_err = AOMMAX(avg_intra_err / (double)counti, 0.001);
+        avg_coded_err /= AOMMAX(avg_coded_err / (double)counti, 0.001);
+        avg_coeff /= AOMMIN(avg_intra_err / (double)counti, 0.99999);
+        int count_coded = 0, count_grad = 0;
+        for (j = lasti + 1; j <= regions[k].last; j++) {
+          int intra_close =
+              fabs(stats[j].intra_error - avg_intra_err) / avg_intra_err < 0.1;
+          int coded_close =
+              fabs(stats[j].coded_error - avg_coded_err) / avg_coded_err < 0.15;
+          int grad_small = fabs(grad[j]) / avg_coded_err < 0.05;
+          int coded_small = stats[j].coded_error / avg_intra_err < 0.03;
+          int coeff_close =
+              (1 - coeff[j]) / (1 - avg_coeff) < 1.5 || coeff[j] > 0.995;
+          if (!coeff_close || (!coded_close && !coded_small)) count_coded--;
+          if (!grad_small && !coded_small) count_grad--;
+
+          if (intra_close && count_coded >= 0 && count_grad >= 0) {
+            // this frame probably belongs to the previous stable region
+            regions[k - 1].last = j;
+            regions[k].start = j + 1;
+          } else {
+            break;
+          }
+        }
+      }
+    }  // if k > 0
+    if (k < *num_regions - 1) {
+      // Adjust next boundary.
+      // First find the average intra/coded error in the next neighborhood.
+      double avg_intra_err = 0, avg_coded_err = 0, avg_coeff = 0;
+      int starti = regions[k + 1].start;
+      int lasti = AOMMIN(regions[k + 1].last - 1,
+                         regions[k + 1].start + WINDOW_SIZE - 1);
+      int counti = 0;
+      for (i = starti; i <= lasti; i++) {
+        avg_intra_err += stats[i].intra_error;
+        avg_coded_err += stats[i + 1].coded_error;
+        avg_coeff += coeff[i];
+        counti++;
+      }
+      if (counti > 0) {
+        avg_intra_err = AOMMAX(avg_intra_err / (double)counti, 0.001);
+        avg_coded_err /= AOMMAX(avg_coded_err / (double)counti, 0.001);
+        avg_coeff /= AOMMIN(avg_intra_err / (double)counti, 0.99999);
+        // At the boundary, coded error is large, but still the frame is stable
+        int count_coded = 1, count_grad = 1;
+        for (j = starti - 1; j >= regions[k].start; j--) {
+          int intra_close =
+              fabs(stats[j].intra_error - avg_intra_err) / avg_intra_err < 0.1;
+          int coded_close =
+              fabs(stats[j + 1].coded_error - avg_coded_err) / avg_coded_err <
+              0.15;
+          int grad_small = fabs(grad[j + 1]) / avg_coded_err < 0.05;
+          int coded_small = stats[j + 1].coded_error / avg_intra_err < 0.03;
+          int coeff_close =
+              (1 - coeff[j + 1]) / (1 - avg_coeff) < 1.5 || coeff[j] > 0.995;
+          if (!coeff_close || (!coded_close && !coded_small)) count_coded--;
+          if (!grad_small && !coded_small) count_grad--;
+          if (intra_close && count_coded >= 0 && count_grad >= 0) {
+            // this frame probably belongs to the next stable region
+            regions[k + 1].start = j;
+            regions[k].last = j - 1;
+          } else {
+            break;
+          }
+        }
+      }
+    }  // if k < *num_regions - 1
+  }    // end of loop over all regions
+
+  cleanup_regions(regions, num_regions);
+  remove_short_regions(regions, num_regions, HIGH_VAR_REGION, HALF_WIN);
+  get_region_stats(stats, is_flash, regions, coeff, *num_regions);
+
+  // If a stable regions has higher error than neighboring high var regions,
+  // or if the stable region has a lower average correlation,
+  // then it should be merged with them
+  k = 0;
+  while (k < *num_regions && (*num_regions) > 1) {
+    if (regions[k].type == STABLE_REGION &&
+        ((k > 0 &&  // previous regions
+          (regions[k].avg_coded_err > regions[k - 1].avg_coded_err ||
+           regions[k].avg_cor_coeff < regions[k - 1].avg_cor_coeff)) ||
+         (k < *num_regions - 1 &&  // next region
+          (regions[k].avg_coded_err > regions[k + 1].avg_coded_err ||
+           regions[k].avg_cor_coeff < regions[k + 1].avg_cor_coeff)))) {
+      // merge current region with the previous and next regions
+      remove_region(2, regions, num_regions, &k);
+      analyze_region(stats, k - 1, regions, coeff);
+    } else {
+      k++;
+    }
+  }
+
+  remove_short_regions(regions, num_regions, STABLE_REGION, WINDOW_SIZE);
+  remove_short_regions(regions, num_regions, HIGH_VAR_REGION, HALF_WIN);
+}
+
 #endif
 
 /*!\brief Determine the length of future GF groups.