blob: e92ae56639a4be575df65a5d91755cb985645621 [file] [log] [blame]
Parker Schuh9e1d1692019-02-24 14:34:04 -08001#include <fstream>
2
3#include "aos/logging/implementations.h"
4#include "aos/logging/logging.h"
5#include "aos/vision/blob/codec.h"
6#include "aos/vision/blob/find_blob.h"
7#include "aos/vision/events/socket_types.h"
8#include "aos/vision/events/udp.h"
9#include "aos/vision/image/image_dataset.h"
10#include "aos/vision/image/image_stream.h"
11#include "aos/vision/image/reader.h"
12#include "y2019/vision/target_finder.h"
13
14#undef CHECK_NOTNULL
15#undef CHECK_OP
16#undef PCHECK
17// CERES Clashes with logging symbols...
18#include "ceres/ceres.h"
19
20using ::aos::events::DataSocket;
21using ::aos::events::RXUdpSocket;
22using ::aos::events::TCPServer;
23using ::aos::vision::DataRef;
24using ::aos::vision::Int32Codec;
25using ::aos::monotonic_clock;
26using aos::vision::Segment;
27
28using ceres::NumericDiffCostFunction;
29using ceres::CENTRAL;
30using ceres::CostFunction;
31using ceres::Problem;
32using ceres::Solver;
33using ceres::Solve;
34
35namespace y2019 {
36namespace vision {
37
38constexpr double kInchesToMeters = 0.0254;
39
40template <size_t k, size_t n, size_t n2, typename T>
41T *MakeFunctor(T &&t) {
42 return new T(std::move(t));
43}
44
45std::array<double, 3> GetError(const double *const extrinsics,
46 const double *const geometry, int i) {
47 auto ex = ExtrinsicParams::get(extrinsics);
48
49 double s = sin(geometry[2] + ex.r2);
50 double c = cos(geometry[2] + ex.r2);
51
52 // TODO: Generalize this from being just for a single calibration.
53 double dx = 12.5 + 26.0 + i - (geometry[0] + c * ex.z) / kInchesToMeters;
54 double dy = 12.0 - (geometry[1] + s * ex.z) / kInchesToMeters;
55 double dz = 28.5 - (geometry[3] + ex.y) / kInchesToMeters;
56 return {{dx, dy, dz}};
57}
58
59void main(int argc, char **argv) {
60 (void)argc;
61 (void)argv;
62 using namespace y2019::vision;
63 // gflags::ParseCommandLineFlags(&argc, &argv, false);
64 ::aos::logging::Init();
65 ::aos::logging::AddImplementation(
66 new ::aos::logging::StreamLogImplementation(stderr));
67
68 TargetFinder finder_;
69
70 ceres::Problem problem;
71
72 struct Sample {
73 std::unique_ptr<double[]> extrinsics;
74 int i;
75 };
76 std::vector<Sample> all_extrinsics;
77 double intrinsics[IntrinsicParams::kNumParams];
78 IntrinsicParams().set(&intrinsics[0]);
79
80 // To know the meaning, see the printout below...
81 constexpr size_t GeometrykNumParams = 4;
82 double geometry[GeometrykNumParams] = {0, 0, 0, 0};
83
84 Solver::Options options;
85 options.minimizer_progress_to_stdout = false;
86 Solver::Summary summary;
87
88 std::cout << summary.BriefReport() << "\n";
89 auto intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
90 std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";\n";
91 std::cout << "fl = " << intrinsics_.focal_length << ";\n";
92 std::cout << "error = " << summary.final_cost << ";\n";
93
94 int nimgs = 56;
95 for (int i = 0; i < nimgs; ++i) {
96 auto frame = aos::vision::LoadFile(
97 "/home/parker/data/frc/2019_calibration/cam4_0/debug_viewer_jpeg_" +
98 std::to_string(i) + ".yuyv");
99
100 aos::vision::ImageFormat fmt{640, 480};
101 aos::vision::BlobList imgs = aos::vision::FindBlobs(
102 aos::vision::DoThresholdYUYV(fmt, frame.data.data(), 120));
103 finder_.PreFilter(&imgs);
104
105 bool verbose = false;
106 std::vector<std::vector<Segment<2>>> raw_polys;
107 for (const RangeImage &blob : imgs) {
108 std::vector<Segment<2>> polygon = finder_.FillPolygon(blob, verbose);
109 if (polygon.empty()) {
110 } else {
111 raw_polys.push_back(polygon);
112 }
113 }
114
115 // Calculate each component side of a possible target.
116 std::vector<TargetComponent> target_component_list =
117 finder_.FillTargetComponentList(raw_polys);
118
119 // Put the compenents together into targets.
120 std::vector<Target> target_list =
121 finder_.FindTargetsFromComponents(target_component_list, verbose);
122
123 // Use the solver to generate an intermediate version of our results.
124 std::vector<IntermediateResult> results;
125 for (const Target &target : target_list) {
126 auto target_value = target.toPointList();
127 auto template_value = finder_.GetTemplateTarget().toPointList();
128
129 auto *extrinsics = new double[ExtrinsicParams::kNumParams];
130 ExtrinsicParams().set(extrinsics);
131 all_extrinsics.push_back({std::unique_ptr<double[]>(extrinsics), i});
132
133 for (size_t j = 0; j < 8; ++j) {
134 auto temp = template_value[j];
135 auto targ = target_value[j];
136
137 auto ftor = [temp, targ, i](const double *const intrinsics,
138 const double *const extrinsics,
139 double *residual) {
140 auto in = IntrinsicParams::get(intrinsics);
141 auto ex = ExtrinsicParams::get(extrinsics);
142 auto pt = targ - Project(temp, in, ex);
143 residual[0] = pt.x();
144 residual[1] = pt.y();
145 return true;
146 };
147 problem.AddResidualBlock(
148 new NumericDiffCostFunction<decltype(ftor), CENTRAL, 2,
149 IntrinsicParams::kNumParams,
150 ExtrinsicParams::kNumParams>(
151 new decltype(ftor)(std::move(ftor))),
152 NULL, &intrinsics[0], extrinsics);
153 }
154
155 auto ftor = [i](const double *const extrinsics,
156 const double *const geometry, double *residual) {
157 auto err = GetError(extrinsics, geometry, i);
158 residual[0] = 32.0 * err[0];
159 residual[1] = 32.0 * err[1];
160 residual[2] = 32.0 * err[2];
161 return true;
162 };
163
164 problem.AddResidualBlock(
165 new NumericDiffCostFunction<decltype(ftor), CENTRAL, 3,
166 ExtrinsicParams::kNumParams,
167 GeometrykNumParams>(
168 new decltype(ftor)(std::move(ftor))),
169 NULL, extrinsics, &geometry[0]);
170 }
171 }
172 // TODO: Debug solver convergence?
173 Solve(options, &problem, &summary);
174 Solve(options, &problem, &summary);
175 Solve(options, &problem, &summary);
176
177 {
178 std::cout << summary.BriefReport() << "\n";
179 auto intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
180 std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";\n";
181 std::cout << "fl = " << intrinsics_.focal_length << ";\n";
182 std::cout << "error = " << summary.final_cost << ";\n";
183
184 std::cout << "camera_height = " << geometry[3] / kInchesToMeters << "\n";
185 std::cout << "camera_angle = " << geometry[2] * 180 / M_PI << "\n";
186 std::cout << "camera_x = " << geometry[0] / kInchesToMeters << "\n";
187 std::cout << "camera_y = " << geometry[1] / kInchesToMeters << "\n";
188 std::cout << "camera_barrel = " << intrinsics_.barrel_mount * 180.0 / M_PI
189 << "\n";
190
191 for (auto &sample : all_extrinsics) {
192 int i = sample.i;
193 double *data = sample.extrinsics.get();
194
195 auto extn = ExtrinsicParams::get(data);
196
197 auto err = GetError(data, &geometry[0], i);
198
199 std::cout << i << ", ";
200 std::cout << extn.z / kInchesToMeters << ", ";
201 std::cout << extn.y / kInchesToMeters << ", ";
202 std::cout << extn.r1 * 180 / M_PI << ", ";
203 std::cout << extn.r2 * 180 / M_PI << ", ";
204 // TODO: Methodology problem: This should really have a separate solve for
205 // extrinsics...
206 std::cout << err[0] << ", ";
207 std::cout << err[1] << ", ";
208 std::cout << err[2] << "\n";
209 }
210 }
211}
212
213} // namespace y2019
214} // namespace vision
215
216int main(int argc, char **argv) { y2019::vision::main(argc, argv); }