Auto Exposure:

  - Filter results down to the three best.
  - Changing exposure based on distance to the target or at random.
  - Plumb exposure through the debug framework.
  - Refactor into target finder.

Change-Id: Ia083f56859938bf0825472fd15d248c545f6f2fc
diff --git a/y2019/vision/target_finder.cc b/y2019/vision/target_finder.cc
index 5860e5c..f6e5ac4 100644
--- a/y2019/vision/target_finder.cc
+++ b/y2019/vision/target_finder.cc
@@ -555,6 +555,12 @@
       filtered.emplace_back(updatable_result);
     }
   }
+
+  // Sort the target list so that the widest (ie closest) target is first.
+  sort(filtered.begin(), filtered.end(),
+       [](const IntermediateResult &a, const IntermediateResult &b)
+           -> bool { return a.target_width > b.target_width; });
+
   frame_count_++;
   if (!filtered.empty()) {
     valid_result_count_++;
@@ -570,5 +576,61 @@
   return filtered;
 }
 
+bool TargetFinder::TestExposure(const std::vector<IntermediateResult> &results,
+                                int *desired_exposure) {
+  // TODO(ben): Add these values to config file.
+  constexpr double low_dist = 0.8;
+  constexpr double high_dist = 2.0;
+  constexpr int low_exposure  = 60;
+  constexpr int mid_exposure  = 300;
+  constexpr int high_exposure = 500;
+
+  bool needs_update = false;
+  if (results.size() > 0) {
+    // We are seeing a target so lets use an exposure
+    // based on the distance to that target.
+    // First result should always be the closest target.
+    if (results[0].extrinsics.z < low_dist) {
+      *desired_exposure = low_exposure;
+    } else if (results[0].extrinsics.z > high_dist) {
+      *desired_exposure = high_exposure;
+    } else {
+      *desired_exposure = mid_exposure;
+    }
+    if (*desired_exposure != current_exposure_) {
+      needs_update = true;
+      current_exposure_ = *desired_exposure;
+    }
+  } else {
+    // We don't see a target, but part of the problem might
+    // be the exposure setting. Lets try changing it and see
+    // if things get better.
+    const int offset = std::rand() % 10;
+
+    // At random with 3/X probability try a higher or lower.
+    if (offset == 0) {
+      if (low_exposure != current_exposure_) {
+        needs_update = true;
+        current_exposure_ = low_exposure;
+        *desired_exposure = low_exposure;
+      }
+    } else if (offset == 1) {
+      if (mid_exposure != current_exposure_) {
+        needs_update = true;
+        current_exposure_ = mid_exposure;
+        *desired_exposure = mid_exposure;
+      }
+    } else if (offset == 2) {
+      if (high_exposure != current_exposure_) {
+        needs_update = true;
+        current_exposure_ = high_exposure;
+        *desired_exposure = high_exposure;
+      }
+    }
+    // If one of our cases is not hit don't change anything.
+  }
+  return needs_update;
+}
+
 }  // namespace vision
 }  // namespace y2019