Some updates to debug_window/threshold to support yuyv frames.

Also some errata improvements to image_types, segment, vector.

Change-Id: Ia7b32a469c20726c90b6148acf14151458f79e7d
diff --git a/aos/vision/blob/threshold.h b/aos/vision/blob/threshold.h
index 8b6051f..eef5b20 100644
--- a/aos/vision/blob/threshold.h
+++ b/aos/vision/blob/threshold.h
@@ -9,15 +9,15 @@
 
 // ThresholdFn should be a lambda.
 template <typename ThresholdFn>
-RangeImage DoThreshold(const ImagePtr &img, ThresholdFn &&fn) {
+RangeImage DoThreshold(ImageFormat fmt, ThresholdFn &&fn) {
   std::vector<std::vector<ImageRange>> ranges;
-  ranges.reserve(img.fmt().h);
-  for (int y = 0; y < img.fmt().h; ++y) {
+  ranges.reserve(fmt.h);
+  for (int y = 0; y < fmt.h; ++y) {
     bool p_score = false;
     int pstart = -1;
     std::vector<ImageRange> rngs;
-    for (int x = 0; x < img.fmt().w; ++x) {
-      if (fn(img.get_px(x, y)) != p_score) {
+    for (int x = 0; x < fmt.w; ++x) {
+      if (fn(x, y) != p_score) {
         if (p_score) {
           rngs.emplace_back(ImageRange(pstart, x));
         } else {
@@ -27,13 +27,29 @@
       }
     }
     if (p_score) {
-      rngs.emplace_back(ImageRange(pstart, img.fmt().w));
+      rngs.emplace_back(ImageRange(pstart, fmt.w));
     }
     ranges.push_back(rngs);
   }
   return RangeImage(0, std::move(ranges));
 }
 
+// ThresholdFn should be a lambda.
+template <typename ThresholdFn>
+RangeImage DoThreshold(const ImagePtr &img, ThresholdFn &&fn) {
+  return DoThreshold(img.fmt(),
+                     [&](int x, int y) { return fn(img.get_px(x, y)); });
+}
+
+// YUYV image types:
+inline RangeImage DoThresholdYUYV(ImageFormat fmt, const char *data,
+                                  uint8_t value) {
+  return DoThreshold(fmt, [&](int x, int y) {
+    uint8_t v = data[y * fmt.w * 2 + x * 2];
+    return v > value;
+  });
+}
+
 }  // namespace vision
 }  // namespace aos
 
diff --git a/aos/vision/debug/debug_framework.cc b/aos/vision/debug/debug_framework.cc
index 46f5400..1d94217 100644
--- a/aos/vision/debug/debug_framework.cc
+++ b/aos/vision/debug/debug_framework.cc
@@ -58,7 +58,7 @@
       InstallKeyPress(key_press);
     }
     if (GetScreenHeight() < 1024) {
-      view_.SetScale(0.75);
+      view_.SetScale(1.0);
     }
   }
 
@@ -70,6 +70,17 @@
     return HandleBlobs(FindBlobs(filter_->Threshold(view_.img())), fmt);
   }
 
+  bool NewImage(ImageFormat fmt,
+                const std::function<bool(ImagePtr data)> &process) override {
+    auto value = view_.img();
+    if (!value.fmt().Equals(fmt)) {
+      view_.SetFormatAndClear(fmt);
+    }
+    process(view_.img());
+
+    return HandleBlobs(FindBlobs(filter_->Threshold(view_.img())), fmt);
+  }
+
   bool NewBlobList(BlobList blob_list, ImageFormat fmt) override {
     view_.SetFormatAndClear(fmt);
 
diff --git a/aos/vision/debug/debug_framework.h b/aos/vision/debug/debug_framework.h
index d5b345f..a46812f 100644
--- a/aos/vision/debug/debug_framework.h
+++ b/aos/vision/debug/debug_framework.h
@@ -55,6 +55,9 @@
   // if the frame is "interesting" ie has a target.
   virtual bool NewJpeg(DataRef data) = 0;
 
+  virtual bool NewImage(ImageFormat fmt,
+                        const std::function<bool(ImagePtr data)> &process) = 0;
+
   virtual bool NewBlobList(BlobList blob_list, ImageFormat fmt) = 0;
 
   virtual bool JustCheckForTarget(BlobList imgs, ImageFormat fmt) = 0;
diff --git a/aos/vision/debug/jpeg_list-source.cc b/aos/vision/debug/jpeg_list-source.cc
index 2dbbe44..d3e1006 100644
--- a/aos/vision/debug/jpeg_list-source.cc
+++ b/aos/vision/debug/jpeg_list-source.cc
@@ -65,16 +65,22 @@
           if (jpeg_filename[i] == '#') return;
           if (jpeg_filename[i] != ' ') break;
         }
+        bool is_jpeg = true;
+        size_t l = jpeg_filename.size();
+        if (l > 4 && jpeg_filename[l - 1] == 'v') {
+          is_jpeg = false;
+        }
         if (jpeg_filename[0] == '/') {
-          images_.emplace_back(GetFileContents(jpeg_filename));
+          images_.emplace_back(Frame{is_jpeg, GetFileContents(jpeg_filename)});
         } else {
-          images_.emplace_back(GetFileContents(basename + jpeg_filename));
+          images_.emplace_back(
+              Frame{is_jpeg, GetFileContents(basename + jpeg_filename)});
         }
       }();
     }
     fprintf(stderr, "loaded %lu items\n", images_.size());
     if (!images_.empty()) {
-      interface_->NewJpeg(images_[idx_]);
+      SetCurrentFrame();
       interface_->InstallKeyPress([this](uint32_t keyval) {
         if (keyval == GDK_KEY_Left && idx_ > 0) {
           --idx_;
@@ -83,11 +89,30 @@
         } else {
           return;
         }
-        interface_->NewJpeg(images_[idx_]);
+        SetCurrentFrame();
       });
     }
   }
 
+  void SetCurrentFrame() {
+    const auto &frame = images_[idx_];
+    if (frame.is_jpeg) {
+      interface_->NewJpeg(frame.data);
+    } else {
+      const auto &data = frame.data;
+      interface_->NewImage({640, 480},
+                           [&](ImagePtr img_data) {
+                             for (int y = 0; y < 480; ++y) {
+                               for (int x = 0; x < 640; ++x) {
+                                 uint8_t v = data[y * 640 * 2 + x * 2 + 0];
+                                 img_data.get_px(x, y) = PixelRef{v, v, v};
+                               }
+                             }
+                             return false;
+                           });
+    }
+  }
+
   const char *GetHelpMessage() override {
     return &R"(
     format_spec is the name of a file with each jpeg filename on a new line.
@@ -98,7 +123,11 @@
 
  private:
   DebugFrameworkInterface *interface_ = nullptr;
-  std::vector<std::string> images_;
+  struct Frame {
+    bool is_jpeg = true;
+    std::string data;
+  };
+  std::vector<Frame> images_;
   size_t idx_ = 0;
 };
 
diff --git a/aos/vision/debug/overlay.h b/aos/vision/debug/overlay.h
index 0122c94..8eb8a23 100644
--- a/aos/vision/debug/overlay.h
+++ b/aos/vision/debug/overlay.h
@@ -121,8 +121,12 @@
 
   // build a segment for this line
   void AddLine(Vector<2> st, Vector<2> ed, PixelRef newColor) {
-    lines_.emplace_back(
-        std::pair<Segment<2>, PixelRef>(Segment<2>(st, ed), newColor));
+    AddLine(Segment<2>(st, ed), newColor);
+  }
+
+  // draw a segment.
+  void AddLine(Segment<2> seg, PixelRef newColor) {
+    lines_.emplace_back(std::pair<Segment<2>, PixelRef>(seg, newColor));
   }
 
   void DrawCross(aos::vision::Vector<2> center, int width,
diff --git a/aos/vision/image/image_types.h b/aos/vision/image/image_types.h
index d4ddbf2..c50400a 100644
--- a/aos/vision/image/image_types.h
+++ b/aos/vision/image/image_types.h
@@ -96,6 +96,7 @@
   }
 
   const ImageFormat &fmt() const { return fmt_; }
+  const ImageType *data() const { return data_.get(); }
   ImageType *data() { return data_.get(); }
 
  private:
diff --git a/aos/vision/math/segment.h b/aos/vision/math/segment.h
index 706beaf..d6927cb 100644
--- a/aos/vision/math/segment.h
+++ b/aos/vision/math/segment.h
@@ -44,7 +44,7 @@
   }
 
   // Intersect two lines in a plane.
-  Vector<2> Intersect(const Segment<2> &other) {
+  Vector<2> Intersect(const Segment<2> &other) const {
     static_assert(Size == 2, "Only works for size == 2");
     double x1 = A_.x();
     double y1 = A_.y();
diff --git a/aos/vision/math/vector.h b/aos/vision/math/vector.h
index 3205236..642c63f 100644
--- a/aos/vision/math/vector.h
+++ b/aos/vision/math/vector.h
@@ -186,6 +186,11 @@
     return tmp.MagSqr();
   }
 
+  // Returns the distance between this and other.
+  double DistanceTo(const Vector<size> &other) const {
+    return std::sqrt(SquaredDistanceTo(other));
+  }
+
  private:
   // The actual data.
   ::Eigen::Matrix<double, 1, size> data_;
@@ -195,6 +200,17 @@
 inline double PointsCrossProduct(const Vector<2> &a, const Vector<2> &b) {
   return a.x() * b.y() - a.y() * b.x();
 }
+
+// Rotates along the z, y, and then x axis (all radians).
+inline Vector<3> Rotate(double rx, double ry, double rz, const Vector<3> vec) {
+  ::Eigen::AngleAxis<double> ax(rx, ::Eigen::Vector3d(1.0, 0.0, 0.0));
+  ::Eigen::AngleAxis<double> ay(ry, ::Eigen::Vector3d(0.0, 1.0, 0.0));
+  ::Eigen::AngleAxis<double> az(rz, ::Eigen::Vector3d(0.0, 0.0, 1.0));
+  Vector<3> result;
+  result.SetData(ax * ay * az * vec.GetData());
+  return result;
+}
+
 // scalar multiply
 template <int Size>
 inline Vector<Size> operator*(const double &lhs, Vector<Size> &rhs) {