Adding global_calibration for finding the relative position of the camera inside the robot.
Change-Id: I98a9fb4544f17969c47b519114fb8ecf8e412329
diff --git a/y2019/vision/global_calibration.cc b/y2019/vision/global_calibration.cc
new file mode 100644
index 0000000..e92ae56
--- /dev/null
+++ b/y2019/vision/global_calibration.cc
@@ -0,0 +1,216 @@
+#include <fstream>
+
+#include "aos/logging/implementations.h"
+#include "aos/logging/logging.h"
+#include "aos/vision/blob/codec.h"
+#include "aos/vision/blob/find_blob.h"
+#include "aos/vision/events/socket_types.h"
+#include "aos/vision/events/udp.h"
+#include "aos/vision/image/image_dataset.h"
+#include "aos/vision/image/image_stream.h"
+#include "aos/vision/image/reader.h"
+#include "y2019/vision/target_finder.h"
+
+#undef CHECK_NOTNULL
+#undef CHECK_OP
+#undef PCHECK
+// CERES Clashes with logging symbols...
+#include "ceres/ceres.h"
+
+using ::aos::events::DataSocket;
+using ::aos::events::RXUdpSocket;
+using ::aos::events::TCPServer;
+using ::aos::vision::DataRef;
+using ::aos::vision::Int32Codec;
+using ::aos::monotonic_clock;
+using aos::vision::Segment;
+
+using ceres::NumericDiffCostFunction;
+using ceres::CENTRAL;
+using ceres::CostFunction;
+using ceres::Problem;
+using ceres::Solver;
+using ceres::Solve;
+
+namespace y2019 {
+namespace vision {
+
+constexpr double kInchesToMeters = 0.0254;
+
+template <size_t k, size_t n, size_t n2, typename T>
+T *MakeFunctor(T &&t) {
+ return new T(std::move(t));
+}
+
+std::array<double, 3> GetError(const double *const extrinsics,
+ const double *const geometry, int i) {
+ auto ex = ExtrinsicParams::get(extrinsics);
+
+ double s = sin(geometry[2] + ex.r2);
+ double c = cos(geometry[2] + ex.r2);
+
+ // TODO: Generalize this from being just for a single calibration.
+ double dx = 12.5 + 26.0 + i - (geometry[0] + c * ex.z) / kInchesToMeters;
+ double dy = 12.0 - (geometry[1] + s * ex.z) / kInchesToMeters;
+ double dz = 28.5 - (geometry[3] + ex.y) / kInchesToMeters;
+ return {{dx, dy, dz}};
+}
+
+void main(int argc, char **argv) {
+ (void)argc;
+ (void)argv;
+ using namespace y2019::vision;
+ // gflags::ParseCommandLineFlags(&argc, &argv, false);
+ ::aos::logging::Init();
+ ::aos::logging::AddImplementation(
+ new ::aos::logging::StreamLogImplementation(stderr));
+
+ TargetFinder finder_;
+
+ ceres::Problem problem;
+
+ struct Sample {
+ std::unique_ptr<double[]> extrinsics;
+ int i;
+ };
+ std::vector<Sample> all_extrinsics;
+ double intrinsics[IntrinsicParams::kNumParams];
+ IntrinsicParams().set(&intrinsics[0]);
+
+ // To know the meaning, see the printout below...
+ constexpr size_t GeometrykNumParams = 4;
+ double geometry[GeometrykNumParams] = {0, 0, 0, 0};
+
+ Solver::Options options;
+ options.minimizer_progress_to_stdout = false;
+ Solver::Summary summary;
+
+ std::cout << summary.BriefReport() << "\n";
+ auto intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
+ std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";\n";
+ std::cout << "fl = " << intrinsics_.focal_length << ";\n";
+ std::cout << "error = " << summary.final_cost << ";\n";
+
+ int nimgs = 56;
+ for (int i = 0; i < nimgs; ++i) {
+ auto frame = aos::vision::LoadFile(
+ "/home/parker/data/frc/2019_calibration/cam4_0/debug_viewer_jpeg_" +
+ std::to_string(i) + ".yuyv");
+
+ aos::vision::ImageFormat fmt{640, 480};
+ aos::vision::BlobList imgs = aos::vision::FindBlobs(
+ aos::vision::DoThresholdYUYV(fmt, frame.data.data(), 120));
+ finder_.PreFilter(&imgs);
+
+ bool verbose = false;
+ std::vector<std::vector<Segment<2>>> raw_polys;
+ for (const RangeImage &blob : imgs) {
+ std::vector<Segment<2>> polygon = finder_.FillPolygon(blob, verbose);
+ if (polygon.empty()) {
+ } else {
+ raw_polys.push_back(polygon);
+ }
+ }
+
+ // Calculate each component side of a possible target.
+ std::vector<TargetComponent> target_component_list =
+ finder_.FillTargetComponentList(raw_polys);
+
+ // Put the compenents together into targets.
+ std::vector<Target> target_list =
+ finder_.FindTargetsFromComponents(target_component_list, verbose);
+
+ // Use the solver to generate an intermediate version of our results.
+ std::vector<IntermediateResult> results;
+ for (const Target &target : target_list) {
+ auto target_value = target.toPointList();
+ auto template_value = finder_.GetTemplateTarget().toPointList();
+
+ auto *extrinsics = new double[ExtrinsicParams::kNumParams];
+ ExtrinsicParams().set(extrinsics);
+ all_extrinsics.push_back({std::unique_ptr<double[]>(extrinsics), i});
+
+ for (size_t j = 0; j < 8; ++j) {
+ auto temp = template_value[j];
+ auto targ = target_value[j];
+
+ auto ftor = [temp, targ, i](const double *const intrinsics,
+ const double *const extrinsics,
+ double *residual) {
+ auto in = IntrinsicParams::get(intrinsics);
+ auto ex = ExtrinsicParams::get(extrinsics);
+ auto pt = targ - Project(temp, in, ex);
+ residual[0] = pt.x();
+ residual[1] = pt.y();
+ return true;
+ };
+ problem.AddResidualBlock(
+ new NumericDiffCostFunction<decltype(ftor), CENTRAL, 2,
+ IntrinsicParams::kNumParams,
+ ExtrinsicParams::kNumParams>(
+ new decltype(ftor)(std::move(ftor))),
+ NULL, &intrinsics[0], extrinsics);
+ }
+
+ auto ftor = [i](const double *const extrinsics,
+ const double *const geometry, double *residual) {
+ auto err = GetError(extrinsics, geometry, i);
+ residual[0] = 32.0 * err[0];
+ residual[1] = 32.0 * err[1];
+ residual[2] = 32.0 * err[2];
+ return true;
+ };
+
+ problem.AddResidualBlock(
+ new NumericDiffCostFunction<decltype(ftor), CENTRAL, 3,
+ ExtrinsicParams::kNumParams,
+ GeometrykNumParams>(
+ new decltype(ftor)(std::move(ftor))),
+ NULL, extrinsics, &geometry[0]);
+ }
+ }
+ // TODO: Debug solver convergence?
+ Solve(options, &problem, &summary);
+ Solve(options, &problem, &summary);
+ Solve(options, &problem, &summary);
+
+ {
+ std::cout << summary.BriefReport() << "\n";
+ auto intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
+ std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";\n";
+ std::cout << "fl = " << intrinsics_.focal_length << ";\n";
+ std::cout << "error = " << summary.final_cost << ";\n";
+
+ std::cout << "camera_height = " << geometry[3] / kInchesToMeters << "\n";
+ std::cout << "camera_angle = " << geometry[2] * 180 / M_PI << "\n";
+ std::cout << "camera_x = " << geometry[0] / kInchesToMeters << "\n";
+ std::cout << "camera_y = " << geometry[1] / kInchesToMeters << "\n";
+ std::cout << "camera_barrel = " << intrinsics_.barrel_mount * 180.0 / M_PI
+ << "\n";
+
+ for (auto &sample : all_extrinsics) {
+ int i = sample.i;
+ double *data = sample.extrinsics.get();
+
+ auto extn = ExtrinsicParams::get(data);
+
+ auto err = GetError(data, &geometry[0], i);
+
+ std::cout << i << ", ";
+ std::cout << extn.z / kInchesToMeters << ", ";
+ std::cout << extn.y / kInchesToMeters << ", ";
+ std::cout << extn.r1 * 180 / M_PI << ", ";
+ std::cout << extn.r2 * 180 / M_PI << ", ";
+ // TODO: Methodology problem: This should really have a separate solve for
+ // extrinsics...
+ std::cout << err[0] << ", ";
+ std::cout << err[1] << ", ";
+ std::cout << err[2] << "\n";
+ }
+ }
+}
+
+} // namespace y2019
+} // namespace vision
+
+int main(int argc, char **argv) { y2019::vision::main(argc, argv); }