blob: 7ca10118f128a4e119be75549030b28545bad577 [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
Austin Schuhe623f9f2019-03-03 12:24:03 -080020DEFINE_int32(camera_id, -1, "The camera ID to calibrate");
21
Parker Schuh9e1d1692019-02-24 14:34:04 -080022using ::aos::events::DataSocket;
23using ::aos::events::RXUdpSocket;
24using ::aos::events::TCPServer;
25using ::aos::vision::DataRef;
26using ::aos::vision::Int32Codec;
27using ::aos::monotonic_clock;
28using aos::vision::Segment;
29
30using ceres::NumericDiffCostFunction;
31using ceres::CENTRAL;
32using ceres::CostFunction;
33using ceres::Problem;
34using ceres::Solver;
35using ceres::Solve;
36
37namespace y2019 {
38namespace vision {
39
40constexpr double kInchesToMeters = 0.0254;
41
42template <size_t k, size_t n, size_t n2, typename T>
43T *MakeFunctor(T &&t) {
44 return new T(std::move(t));
45}
46
Parker Schuha4e52fb2019-02-24 18:18:15 -080047std::array<double, 3> GetError(const DatasetInfo &info,
48 const double *const extrinsics,
Parker Schuh9e1d1692019-02-24 14:34:04 -080049 const double *const geometry, int i) {
Parker Schuha4e52fb2019-02-24 18:18:15 -080050 auto extrinsic_params = ExtrinsicParams::get(extrinsics);
51 auto geo = CameraGeometry::get(geometry);
Parker Schuh9e1d1692019-02-24 14:34:04 -080052
Parker Schuha4e52fb2019-02-24 18:18:15 -080053 const double s = sin(geo.heading + extrinsic_params.r2);
54 const double c = cos(geo.heading + extrinsic_params.r2);
Parker Schuh9e1d1692019-02-24 14:34:04 -080055
Parker Schuha4e52fb2019-02-24 18:18:15 -080056 // Take the tape measure starting point (this will be at the perimeter of the
57 // robot), add the offset to the first sample, and then add the per sample
58 // offset.
59 const double dx =
60 (info.to_tape_measure_start[0] +
61 (info.beginning_tape_measure_reading + i) *
62 info.tape_measure_direction[0]) /
63 kInchesToMeters -
64 (geo.location[0] + c * extrinsic_params.z) / kInchesToMeters;
65 const double dy =
66 (info.to_tape_measure_start[1] +
67 (info.beginning_tape_measure_reading + i) *
68 info.tape_measure_direction[1]) /
69 kInchesToMeters -
70 (geo.location[1] + s * extrinsic_params.z) / kInchesToMeters;
71
72 constexpr double kCalibrationTargetHeight = 28.5;
73 const double dz = kCalibrationTargetHeight -
74 (geo.location[2] + extrinsic_params.y) / kInchesToMeters;
Parker Schuh9e1d1692019-02-24 14:34:04 -080075 return {{dx, dy, dz}};
76}
77
78void main(int argc, char **argv) {
Parker Schuh9e1d1692019-02-24 14:34:04 -080079 using namespace y2019::vision;
Austin Schuhe623f9f2019-03-03 12:24:03 -080080 gflags::ParseCommandLineFlags(&argc, &argv, false);
Parker Schuha4e52fb2019-02-24 18:18:15 -080081
Parker Schuha4e52fb2019-02-24 18:18:15 -080082 const char *base_directory = "/home/parker/data/frc/2019_calibration/";
83
84 DatasetInfo info = {
Austin Schuhe623f9f2019-03-03 12:24:03 -080085 FLAGS_camera_id,
Parker Schuha4e52fb2019-02-24 18:18:15 -080086 {{12.5 * kInchesToMeters, 0.5 * kInchesToMeters}},
87 {{kInchesToMeters, 0.0}},
88 26,
89 "cam5_0/debug_viewer_jpeg_",
90 59,
91 };
92
Parker Schuh9e1d1692019-02-24 14:34:04 -080093 ::aos::logging::Init();
94 ::aos::logging::AddImplementation(
95 new ::aos::logging::StreamLogImplementation(stderr));
96
97 TargetFinder finder_;
98
99 ceres::Problem problem;
100
101 struct Sample {
102 std::unique_ptr<double[]> extrinsics;
103 int i;
104 };
105 std::vector<Sample> all_extrinsics;
106 double intrinsics[IntrinsicParams::kNumParams];
107 IntrinsicParams().set(&intrinsics[0]);
108
Parker Schuha4e52fb2019-02-24 18:18:15 -0800109 double geometry[CameraGeometry::kNumParams];
110 CameraGeometry().set(&geometry[0]);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800111
112 Solver::Options options;
113 options.minimizer_progress_to_stdout = false;
114 Solver::Summary summary;
115
116 std::cout << summary.BriefReport() << "\n";
117 auto intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
118 std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";\n";
119 std::cout << "fl = " << intrinsics_.focal_length << ";\n";
120 std::cout << "error = " << summary.final_cost << ";\n";
121
Parker Schuha4e52fb2019-02-24 18:18:15 -0800122 for (int i = 0; i < info.num_images; ++i) {
123 auto frame = aos::vision::LoadFile(std::string(base_directory) +
124 info.filename_prefix +
125 std::to_string(i) + ".yuyv");
Parker Schuh9e1d1692019-02-24 14:34:04 -0800126
127 aos::vision::ImageFormat fmt{640, 480};
128 aos::vision::BlobList imgs = aos::vision::FindBlobs(
129 aos::vision::DoThresholdYUYV(fmt, frame.data.data(), 120));
130 finder_.PreFilter(&imgs);
131
132 bool verbose = false;
133 std::vector<std::vector<Segment<2>>> raw_polys;
134 for (const RangeImage &blob : imgs) {
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800135 // Convert blobs to contours in the corrected space.
136 ContourNode* contour = finder_.GetContour(blob);
137 finder_.UnWarpContour(contour);
138 std::vector<Segment<2>> polygon = finder_.FillPolygon(contour, verbose);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800139 if (polygon.empty()) {
140 } else {
141 raw_polys.push_back(polygon);
142 }
143 }
144
145 // Calculate each component side of a possible target.
146 std::vector<TargetComponent> target_component_list =
147 finder_.FillTargetComponentList(raw_polys);
148
149 // Put the compenents together into targets.
150 std::vector<Target> target_list =
151 finder_.FindTargetsFromComponents(target_component_list, verbose);
152
153 // Use the solver to generate an intermediate version of our results.
154 std::vector<IntermediateResult> results;
155 for (const Target &target : target_list) {
Austin Schuh2894e902019-03-03 21:12:46 -0800156 ::std::array<aos::vision::Vector<2>, 8> target_value =
157 target.ToPointList();
158 ::std::array<aos::vision::Vector<2>, 8> template_value =
159 finder_.GetTemplateTarget().ToPointList();
Parker Schuh9e1d1692019-02-24 14:34:04 -0800160
Austin Schuh2894e902019-03-03 21:12:46 -0800161 // TODO(austin): Memory leak below, fix.
162 double *extrinsics = new double[ExtrinsicParams::kNumParams];
Parker Schuh9e1d1692019-02-24 14:34:04 -0800163 ExtrinsicParams().set(extrinsics);
164 all_extrinsics.push_back({std::unique_ptr<double[]>(extrinsics), i});
165
166 for (size_t j = 0; j < 8; ++j) {
Austin Schuh2894e902019-03-03 21:12:46 -0800167 aos::vision::Vector<2> temp = template_value[j];
168 aos::vision::Vector<2> targ = target_value[j];
Parker Schuh9e1d1692019-02-24 14:34:04 -0800169
170 auto ftor = [temp, targ, i](const double *const intrinsics,
171 const double *const extrinsics,
172 double *residual) {
173 auto in = IntrinsicParams::get(intrinsics);
Parker Schuha4e52fb2019-02-24 18:18:15 -0800174 auto extrinsic_params = ExtrinsicParams::get(extrinsics);
175 auto pt = targ - Project(temp, in, extrinsic_params);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800176 residual[0] = pt.x();
177 residual[1] = pt.y();
178 return true;
179 };
180 problem.AddResidualBlock(
181 new NumericDiffCostFunction<decltype(ftor), CENTRAL, 2,
182 IntrinsicParams::kNumParams,
183 ExtrinsicParams::kNumParams>(
184 new decltype(ftor)(std::move(ftor))),
185 NULL, &intrinsics[0], extrinsics);
186 }
187
Parker Schuha4e52fb2019-02-24 18:18:15 -0800188 auto ftor = [&info, i](const double *const extrinsics,
189 const double *const geometry, double *residual) {
190 auto err = GetError(info, extrinsics, geometry, i);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800191 residual[0] = 32.0 * err[0];
192 residual[1] = 32.0 * err[1];
193 residual[2] = 32.0 * err[2];
194 return true;
195 };
196
197 problem.AddResidualBlock(
198 new NumericDiffCostFunction<decltype(ftor), CENTRAL, 3,
199 ExtrinsicParams::kNumParams,
Parker Schuha4e52fb2019-02-24 18:18:15 -0800200 CameraGeometry::kNumParams>(
Parker Schuh9e1d1692019-02-24 14:34:04 -0800201 new decltype(ftor)(std::move(ftor))),
202 NULL, extrinsics, &geometry[0]);
203 }
204 }
205 // TODO: Debug solver convergence?
206 Solve(options, &problem, &summary);
207 Solve(options, &problem, &summary);
208 Solve(options, &problem, &summary);
209
210 {
211 std::cout << summary.BriefReport() << "\n";
212 auto intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
Parker Schuha4e52fb2019-02-24 18:18:15 -0800213 auto geometry_ = CameraGeometry::get(&geometry[0]);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800214 std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";\n";
215 std::cout << "fl = " << intrinsics_.focal_length << ";\n";
216 std::cout << "error = " << summary.final_cost << ";\n";
217
Parker Schuha4e52fb2019-02-24 18:18:15 -0800218 std::cout << "camera_angle = " << geometry_.heading * 180 / M_PI << "\n";
219 std::cout << "camera_x = " << geometry_.location[0] / kInchesToMeters
220 << "\n";
221 std::cout << "camera_y = " << geometry_.location[1] / kInchesToMeters
222 << "\n";
223 std::cout << "camera_z = " << geometry_.location[2] / kInchesToMeters
224 << "\n";
Parker Schuh9e1d1692019-02-24 14:34:04 -0800225 std::cout << "camera_barrel = " << intrinsics_.barrel_mount * 180.0 / M_PI
226 << "\n";
227
228 for (auto &sample : all_extrinsics) {
229 int i = sample.i;
230 double *data = sample.extrinsics.get();
231
232 auto extn = ExtrinsicParams::get(data);
233
Parker Schuha4e52fb2019-02-24 18:18:15 -0800234 auto err = GetError(info, data, &geometry[0], i);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800235
236 std::cout << i << ", ";
237 std::cout << extn.z / kInchesToMeters << ", ";
238 std::cout << extn.y / kInchesToMeters << ", ";
239 std::cout << extn.r1 * 180 / M_PI << ", ";
240 std::cout << extn.r2 * 180 / M_PI << ", ";
241 // TODO: Methodology problem: This should really have a separate solve for
242 // extrinsics...
243 std::cout << err[0] << ", ";
244 std::cout << err[1] << ", ";
245 std::cout << err[2] << "\n";
246 }
247 }
Parker Schuha4e52fb2019-02-24 18:18:15 -0800248
249 CameraCalibration results;
250 results.dataset = info;
251 results.intrinsics = IntrinsicParams::get(&intrinsics[0]);
252 results.geometry = CameraGeometry::get(&geometry[0]);
Austin Schuhe623f9f2019-03-03 12:24:03 -0800253 DumpCameraConstants(info.camera_id, results);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800254}
255
256} // namespace y2019
257} // namespace vision
258
259int main(int argc, char **argv) { y2019::vision::main(argc, argv); }