blob: b72752a6aec4cc86100dd1bdebb9196bada0eacc [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 {
Austin Schuh4d6e9bd2019-03-08 19:54:17 -080039namespace {
Parker Schuh9e1d1692019-02-24 14:34:04 -080040
41constexpr double kInchesToMeters = 0.0254;
42
Austin Schuh4d6e9bd2019-03-08 19:54:17 -080043} // namespace
Parker Schuh9e1d1692019-02-24 14:34:04 -080044
Austin Schuh4d6e9bd2019-03-08 19:54:17 -080045::std::array<double, 3> GetError(const DatasetInfo &info,
46 const double *const extrinsics,
47 const double *const geometry, int i) {
48 const ExtrinsicParams extrinsic_params = ExtrinsicParams::get(extrinsics);
49 const CameraGeometry geo = CameraGeometry::get(geometry);
Parker Schuh9e1d1692019-02-24 14:34:04 -080050
Parker Schuha4e52fb2019-02-24 18:18:15 -080051 const double s = sin(geo.heading + extrinsic_params.r2);
52 const double c = cos(geo.heading + extrinsic_params.r2);
Parker Schuh9e1d1692019-02-24 14:34:04 -080053
Parker Schuha4e52fb2019-02-24 18:18:15 -080054 // Take the tape measure starting point (this will be at the perimeter of the
55 // robot), add the offset to the first sample, and then add the per sample
56 // offset.
57 const double dx =
58 (info.to_tape_measure_start[0] +
59 (info.beginning_tape_measure_reading + i) *
60 info.tape_measure_direction[0]) /
61 kInchesToMeters -
62 (geo.location[0] + c * extrinsic_params.z) / kInchesToMeters;
63 const double dy =
64 (info.to_tape_measure_start[1] +
65 (info.beginning_tape_measure_reading + i) *
66 info.tape_measure_direction[1]) /
67 kInchesToMeters -
68 (geo.location[1] + s * extrinsic_params.z) / kInchesToMeters;
69
70 constexpr double kCalibrationTargetHeight = 28.5;
71 const double dz = kCalibrationTargetHeight -
72 (geo.location[2] + extrinsic_params.y) / kInchesToMeters;
Parker Schuh9e1d1692019-02-24 14:34:04 -080073 return {{dx, dy, dz}};
74}
75
76void main(int argc, char **argv) {
Parker Schuh9e1d1692019-02-24 14:34:04 -080077 using namespace y2019::vision;
Austin Schuhe623f9f2019-03-03 12:24:03 -080078 gflags::ParseCommandLineFlags(&argc, &argv, false);
Parker Schuha4e52fb2019-02-24 18:18:15 -080079
Parker Schuha4e52fb2019-02-24 18:18:15 -080080 const char *base_directory = "/home/parker/data/frc/2019_calibration/";
81
82 DatasetInfo info = {
Austin Schuhe623f9f2019-03-03 12:24:03 -080083 FLAGS_camera_id,
Parker Schuha4e52fb2019-02-24 18:18:15 -080084 {{12.5 * kInchesToMeters, 0.5 * kInchesToMeters}},
85 {{kInchesToMeters, 0.0}},
86 26,
87 "cam5_0/debug_viewer_jpeg_",
88 59,
89 };
90
Parker Schuh9e1d1692019-02-24 14:34:04 -080091 ::aos::logging::Init();
92 ::aos::logging::AddImplementation(
93 new ::aos::logging::StreamLogImplementation(stderr));
94
Austin Schuh4d6e9bd2019-03-08 19:54:17 -080095 TargetFinder target_finder;
Parker Schuh9e1d1692019-02-24 14:34:04 -080096
97 ceres::Problem problem;
98
99 struct Sample {
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800100 ::std::unique_ptr<double[]> extrinsics;
Parker Schuh9e1d1692019-02-24 14:34:04 -0800101 int i;
102 };
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800103 ::std::vector<Sample> all_extrinsics;
Parker Schuh9e1d1692019-02-24 14:34:04 -0800104 double intrinsics[IntrinsicParams::kNumParams];
105 IntrinsicParams().set(&intrinsics[0]);
106
Parker Schuha4e52fb2019-02-24 18:18:15 -0800107 double geometry[CameraGeometry::kNumParams];
108 CameraGeometry().set(&geometry[0]);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800109
110 Solver::Options options;
111 options.minimizer_progress_to_stdout = false;
112 Solver::Summary summary;
113
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800114 ::std::cout << summary.BriefReport() << "\n";
Austin Schuhcacc6922019-03-06 20:44:30 -0800115 IntrinsicParams intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800116 ::std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";\n";
117 ::std::cout << "fl = " << intrinsics_.focal_length << ";\n";
118 ::std::cout << "error = " << summary.final_cost << ";\n";
Parker Schuh9e1d1692019-02-24 14:34:04 -0800119
Parker Schuha4e52fb2019-02-24 18:18:15 -0800120 for (int i = 0; i < info.num_images; ++i) {
121 auto frame = aos::vision::LoadFile(std::string(base_directory) +
122 info.filename_prefix +
123 std::to_string(i) + ".yuyv");
Parker Schuh9e1d1692019-02-24 14:34:04 -0800124
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800125 const ::aos::vision::ImageFormat fmt{640, 480};
126 ::aos::vision::BlobList imgs =
127 ::aos::vision::FindBlobs(aos::vision::DoThresholdYUYV(
Austin Schuh2851e7f2019-03-06 20:45:19 -0800128 fmt, frame.data.data(), TargetFinder::GetThresholdValue()));
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800129 target_finder.PreFilter(&imgs);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800130
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800131 constexpr bool verbose = false;
132 ::std::vector<std::vector<Segment<2>>> raw_polys;
Parker Schuh9e1d1692019-02-24 14:34:04 -0800133 for (const RangeImage &blob : imgs) {
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800134 // Convert blobs to contours in the corrected space.
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800135 ContourNode *contour = target_finder.GetContour(blob);
136 target_finder.UnWarpContour(contour);
137 const ::std::vector<Segment<2>> polygon =
138 target_finder.FillPolygon(contour, verbose);
139 if (!polygon.empty()) {
Parker Schuh9e1d1692019-02-24 14:34:04 -0800140 raw_polys.push_back(polygon);
141 }
142 }
143
144 // Calculate each component side of a possible target.
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800145 const ::std::vector<TargetComponent> target_component_list =
146 target_finder.FillTargetComponentList(raw_polys);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800147
148 // Put the compenents together into targets.
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800149 const ::std::vector<Target> target_list =
150 target_finder.FindTargetsFromComponents(target_component_list, verbose);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800151
152 // Use the solver to generate an intermediate version of our results.
153 std::vector<IntermediateResult> results;
154 for (const Target &target : target_list) {
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800155 const ::std::array<::aos::vision::Vector<2>, 8> target_value =
Austin Schuh2894e902019-03-03 21:12:46 -0800156 target.ToPointList();
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800157 const ::std::array<::aos::vision::Vector<2>, 8> template_value =
158 target_finder.GetTemplateTarget().ToPointList();
Parker Schuh9e1d1692019-02-24 14:34:04 -0800159
Austin Schuh2894e902019-03-03 21:12:46 -0800160 // TODO(austin): Memory leak below, fix.
161 double *extrinsics = new double[ExtrinsicParams::kNumParams];
Parker Schuh9e1d1692019-02-24 14:34:04 -0800162 ExtrinsicParams().set(extrinsics);
163 all_extrinsics.push_back({std::unique_ptr<double[]>(extrinsics), i});
164
165 for (size_t j = 0; j < 8; ++j) {
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800166 // Template target in target space as documented by GetTemplateTarget()
167 const ::aos::vision::Vector<2> template_point = template_value[j];
168 // Target in pixel space.
169 const ::aos::vision::Vector<2> target_point = target_value[j];
Parker Schuh9e1d1692019-02-24 14:34:04 -0800170
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800171 // Now build up the residual block.
172 auto ftor = [template_point, target_point, i](
173 const double *const intrinsics, const double *const extrinsics,
174 double *residual) {
175 const IntrinsicParams intrinsic_params =
176 IntrinsicParams::get(intrinsics);
177 const ExtrinsicParams extrinsic_params =
178 ExtrinsicParams::get(extrinsics);
179 // Project the target location into pixel coordinates.
180 const ::aos::vision::Vector<2> projected_point =
181 Project(template_point, intrinsic_params, extrinsic_params);
182 const ::aos::vision::Vector<2> residual_point =
183 target_point - projected_point;
184 residual[0] = residual_point.x();
185 residual[1] = residual_point.y();
Parker Schuh9e1d1692019-02-24 14:34:04 -0800186 return true;
187 };
188 problem.AddResidualBlock(
189 new NumericDiffCostFunction<decltype(ftor), CENTRAL, 2,
190 IntrinsicParams::kNumParams,
191 ExtrinsicParams::kNumParams>(
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800192 new decltype(ftor)(::std::move(ftor))),
Parker Schuh9e1d1692019-02-24 14:34:04 -0800193 NULL, &intrinsics[0], extrinsics);
194 }
195
Parker Schuha4e52fb2019-02-24 18:18:15 -0800196 auto ftor = [&info, i](const double *const extrinsics,
197 const double *const geometry, double *residual) {
Austin Schuhcacc6922019-03-06 20:44:30 -0800198 std::array<double, 3> err = GetError(info, extrinsics, geometry, i);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800199 residual[0] = 32.0 * err[0];
200 residual[1] = 32.0 * err[1];
201 residual[2] = 32.0 * err[2];
202 return true;
203 };
204
205 problem.AddResidualBlock(
206 new NumericDiffCostFunction<decltype(ftor), CENTRAL, 3,
207 ExtrinsicParams::kNumParams,
Parker Schuha4e52fb2019-02-24 18:18:15 -0800208 CameraGeometry::kNumParams>(
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800209 new decltype(ftor)(::std::move(ftor))),
Parker Schuh9e1d1692019-02-24 14:34:04 -0800210 NULL, extrinsics, &geometry[0]);
211 }
212 }
213 // TODO: Debug solver convergence?
214 Solve(options, &problem, &summary);
215 Solve(options, &problem, &summary);
216 Solve(options, &problem, &summary);
217
218 {
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800219 ::std::cout << summary.BriefReport() << ::std::endl;
Austin Schuhcacc6922019-03-06 20:44:30 -0800220 IntrinsicParams intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
221 CameraGeometry geometry_ = CameraGeometry::get(&geometry[0]);
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800222 ::std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";"
223 << ::std::endl;
224 ::std::cout << "fl = " << intrinsics_.focal_length << ";" << ::std::endl;
225 ::std::cout << "error = " << summary.final_cost << ";" << ::std::endl;
Parker Schuh9e1d1692019-02-24 14:34:04 -0800226
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800227 ::std::cout << "camera_angle = " << geometry_.heading * 180 / M_PI
228 << ::std::endl;
229 ::std::cout << "camera_x = " << geometry_.location[0] << ::std::endl;
230 ::std::cout << "camera_y = " << geometry_.location[1] << ::std::endl;
231 ::std::cout << "camera_z = " << geometry_.location[2] << ::std::endl;
232 ::std::cout << "camera_barrel = " << intrinsics_.barrel_mount * 180.0 / M_PI
233 << ::std::endl;
Parker Schuh9e1d1692019-02-24 14:34:04 -0800234
Austin Schuhcacc6922019-03-06 20:44:30 -0800235 for (const Sample &sample : all_extrinsics) {
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800236 const int i = sample.i;
Parker Schuh9e1d1692019-02-24 14:34:04 -0800237 double *data = sample.extrinsics.get();
238
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800239 const ExtrinsicParams extrinsic_params = ExtrinsicParams::get(data);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800240
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800241 const ::std::array<double, 3> error =
242 GetError(info, data, &geometry[0], i);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800243
Austin Schuh4d6e9bd2019-03-08 19:54:17 -0800244 ::std::cout << i << ", ";
245 ::std::cout << extrinsic_params.z << "m, ";
246 ::std::cout << extrinsic_params.y << "m, ";
247 ::std::cout << extrinsic_params.r1 * 180 / M_PI << ", ";
248 ::std::cout << extrinsic_params.r2 * 180 / M_PI << ", ";
Parker Schuh9e1d1692019-02-24 14:34:04 -0800249 // TODO: Methodology problem: This should really have a separate solve for
250 // extrinsics...
251 std::cout << err[0] << ", ";
252 std::cout << err[1] << ", ";
253 std::cout << err[2] << "\n";
254 }
255 }
Parker Schuha4e52fb2019-02-24 18:18:15 -0800256
257 CameraCalibration results;
258 results.dataset = info;
259 results.intrinsics = IntrinsicParams::get(&intrinsics[0]);
260 results.geometry = CameraGeometry::get(&geometry[0]);
James Kuszmaule2c71ea2019-03-04 08:14:21 -0800261 DumpCameraConstants("y2019/vision/constants.cc", info.camera_id, results);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800262}
263
264} // namespace y2019
265} // namespace vision
266
267int main(int argc, char **argv) { y2019::vision::main(argc, argv); }