blob: d5f0d4ed0b2a26b4686adeb9d61a22d1784cbf17 [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
Parker Schuha4e52fb2019-02-24 18:18:15 -080045std::array<double, 3> GetError(const DatasetInfo &info,
46 const double *const extrinsics,
Parker Schuh9e1d1692019-02-24 14:34:04 -080047 const double *const geometry, int i) {
Parker Schuha4e52fb2019-02-24 18:18:15 -080048 auto extrinsic_params = ExtrinsicParams::get(extrinsics);
49 auto 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) {
77 (void)argc;
78 (void)argv;
79 using namespace y2019::vision;
80 // gflags::ParseCommandLineFlags(&argc, &argv, false);
Parker Schuha4e52fb2019-02-24 18:18:15 -080081
82 int camera_id = 5;
83 const char *base_directory = "/home/parker/data/frc/2019_calibration/";
84
85 DatasetInfo info = {
86 camera_id,
87 {{12.5 * kInchesToMeters, 0.5 * kInchesToMeters}},
88 {{kInchesToMeters, 0.0}},
89 26,
90 "cam5_0/debug_viewer_jpeg_",
91 59,
92 };
93
Parker Schuh9e1d1692019-02-24 14:34:04 -080094 ::aos::logging::Init();
95 ::aos::logging::AddImplementation(
96 new ::aos::logging::StreamLogImplementation(stderr));
97
98 TargetFinder finder_;
99
100 ceres::Problem problem;
101
102 struct Sample {
103 std::unique_ptr<double[]> extrinsics;
104 int i;
105 };
106 std::vector<Sample> all_extrinsics;
107 double intrinsics[IntrinsicParams::kNumParams];
108 IntrinsicParams().set(&intrinsics[0]);
109
Parker Schuha4e52fb2019-02-24 18:18:15 -0800110 double geometry[CameraGeometry::kNumParams];
111 CameraGeometry().set(&geometry[0]);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800112
113 Solver::Options options;
114 options.minimizer_progress_to_stdout = false;
115 Solver::Summary summary;
116
117 std::cout << summary.BriefReport() << "\n";
118 auto intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
119 std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";\n";
120 std::cout << "fl = " << intrinsics_.focal_length << ";\n";
121 std::cout << "error = " << summary.final_cost << ";\n";
122
Parker Schuha4e52fb2019-02-24 18:18:15 -0800123 for (int i = 0; i < info.num_images; ++i) {
124 auto frame = aos::vision::LoadFile(std::string(base_directory) +
125 info.filename_prefix +
126 std::to_string(i) + ".yuyv");
Parker Schuh9e1d1692019-02-24 14:34:04 -0800127
128 aos::vision::ImageFormat fmt{640, 480};
129 aos::vision::BlobList imgs = aos::vision::FindBlobs(
130 aos::vision::DoThresholdYUYV(fmt, frame.data.data(), 120));
131 finder_.PreFilter(&imgs);
132
133 bool verbose = false;
134 std::vector<std::vector<Segment<2>>> raw_polys;
135 for (const RangeImage &blob : imgs) {
136 std::vector<Segment<2>> polygon = finder_.FillPolygon(blob, verbose);
137 if (polygon.empty()) {
138 } else {
139 raw_polys.push_back(polygon);
140 }
141 }
142
143 // Calculate each component side of a possible target.
144 std::vector<TargetComponent> target_component_list =
145 finder_.FillTargetComponentList(raw_polys);
146
147 // Put the compenents together into targets.
148 std::vector<Target> target_list =
149 finder_.FindTargetsFromComponents(target_component_list, verbose);
150
151 // Use the solver to generate an intermediate version of our results.
152 std::vector<IntermediateResult> results;
153 for (const Target &target : target_list) {
154 auto target_value = target.toPointList();
155 auto template_value = finder_.GetTemplateTarget().toPointList();
156
157 auto *extrinsics = new double[ExtrinsicParams::kNumParams];
158 ExtrinsicParams().set(extrinsics);
159 all_extrinsics.push_back({std::unique_ptr<double[]>(extrinsics), i});
160
161 for (size_t j = 0; j < 8; ++j) {
162 auto temp = template_value[j];
163 auto targ = target_value[j];
164
165 auto ftor = [temp, targ, i](const double *const intrinsics,
166 const double *const extrinsics,
167 double *residual) {
168 auto in = IntrinsicParams::get(intrinsics);
Parker Schuha4e52fb2019-02-24 18:18:15 -0800169 auto extrinsic_params = ExtrinsicParams::get(extrinsics);
170 auto pt = targ - Project(temp, in, extrinsic_params);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800171 residual[0] = pt.x();
172 residual[1] = pt.y();
173 return true;
174 };
175 problem.AddResidualBlock(
176 new NumericDiffCostFunction<decltype(ftor), CENTRAL, 2,
177 IntrinsicParams::kNumParams,
178 ExtrinsicParams::kNumParams>(
179 new decltype(ftor)(std::move(ftor))),
180 NULL, &intrinsics[0], extrinsics);
181 }
182
Parker Schuha4e52fb2019-02-24 18:18:15 -0800183 auto ftor = [&info, i](const double *const extrinsics,
184 const double *const geometry, double *residual) {
185 auto err = GetError(info, extrinsics, geometry, i);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800186 residual[0] = 32.0 * err[0];
187 residual[1] = 32.0 * err[1];
188 residual[2] = 32.0 * err[2];
189 return true;
190 };
191
192 problem.AddResidualBlock(
193 new NumericDiffCostFunction<decltype(ftor), CENTRAL, 3,
194 ExtrinsicParams::kNumParams,
Parker Schuha4e52fb2019-02-24 18:18:15 -0800195 CameraGeometry::kNumParams>(
Parker Schuh9e1d1692019-02-24 14:34:04 -0800196 new decltype(ftor)(std::move(ftor))),
197 NULL, extrinsics, &geometry[0]);
198 }
199 }
200 // TODO: Debug solver convergence?
201 Solve(options, &problem, &summary);
202 Solve(options, &problem, &summary);
203 Solve(options, &problem, &summary);
204
205 {
206 std::cout << summary.BriefReport() << "\n";
207 auto intrinsics_ = IntrinsicParams::get(&intrinsics[0]);
Parker Schuha4e52fb2019-02-24 18:18:15 -0800208 auto geometry_ = CameraGeometry::get(&geometry[0]);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800209 std::cout << "rup = " << intrinsics_.mount_angle * 180 / M_PI << ";\n";
210 std::cout << "fl = " << intrinsics_.focal_length << ";\n";
211 std::cout << "error = " << summary.final_cost << ";\n";
212
Parker Schuha4e52fb2019-02-24 18:18:15 -0800213 std::cout << "camera_angle = " << geometry_.heading * 180 / M_PI << "\n";
214 std::cout << "camera_x = " << geometry_.location[0] / kInchesToMeters
215 << "\n";
216 std::cout << "camera_y = " << geometry_.location[1] / kInchesToMeters
217 << "\n";
218 std::cout << "camera_z = " << geometry_.location[2] / kInchesToMeters
219 << "\n";
Parker Schuh9e1d1692019-02-24 14:34:04 -0800220 std::cout << "camera_barrel = " << intrinsics_.barrel_mount * 180.0 / M_PI
221 << "\n";
222
223 for (auto &sample : all_extrinsics) {
224 int i = sample.i;
225 double *data = sample.extrinsics.get();
226
227 auto extn = ExtrinsicParams::get(data);
228
Parker Schuha4e52fb2019-02-24 18:18:15 -0800229 auto err = GetError(info, data, &geometry[0], i);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800230
231 std::cout << i << ", ";
232 std::cout << extn.z / kInchesToMeters << ", ";
233 std::cout << extn.y / kInchesToMeters << ", ";
234 std::cout << extn.r1 * 180 / M_PI << ", ";
235 std::cout << extn.r2 * 180 / M_PI << ", ";
236 // TODO: Methodology problem: This should really have a separate solve for
237 // extrinsics...
238 std::cout << err[0] << ", ";
239 std::cout << err[1] << ", ";
240 std::cout << err[2] << "\n";
241 }
242 }
Parker Schuha4e52fb2019-02-24 18:18:15 -0800243
244 CameraCalibration results;
245 results.dataset = info;
246 results.intrinsics = IntrinsicParams::get(&intrinsics[0]);
247 results.geometry = CameraGeometry::get(&geometry[0]);
248 DumpCameraConstants(camera_id, results);
Parker Schuh9e1d1692019-02-24 14:34:04 -0800249}
250
251} // namespace y2019
252} // namespace vision
253
254int main(int argc, char **argv) { y2019::vision::main(argc, argv); }