blob: ceac89a636ef31e16312c2466d3c90fb74830b68 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2015 Google Inc. All rights reserved.
3// http://ceres-solver.org/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors may be
14// used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//
29// Author: sameeragarwal@google.com (Sameer Agarwal)
30
31#include "bal_problem.h"
32
33#include <cstdio>
34#include <cstdlib>
35#include <fstream>
36#include <string>
37#include <vector>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080038
Austin Schuh70cc9552019-01-21 19:46:48 -080039#include "Eigen/Core"
40#include "ceres/rotation.h"
41#include "glog/logging.h"
42#include "random.h"
43
44namespace ceres {
45namespace examples {
46namespace {
47typedef Eigen::Map<Eigen::VectorXd> VectorRef;
48typedef Eigen::Map<const Eigen::VectorXd> ConstVectorRef;
49
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080050template <typename T>
Austin Schuh70cc9552019-01-21 19:46:48 -080051void FscanfOrDie(FILE* fptr, const char* format, T* value) {
52 int num_scanned = fscanf(fptr, format, value);
53 if (num_scanned != 1) {
54 LOG(FATAL) << "Invalid UW data file.";
55 }
56}
57
58void PerturbPoint3(const double sigma, double* point) {
59 for (int i = 0; i < 3; ++i) {
60 point[i] += RandNormal() * sigma;
61 }
62}
63
64double Median(std::vector<double>* data) {
65 int n = data->size();
66 std::vector<double>::iterator mid_point = data->begin() + n / 2;
67 std::nth_element(data->begin(), mid_point, data->end());
68 return *mid_point;
69}
70
71} // namespace
72
73BALProblem::BALProblem(const std::string& filename, bool use_quaternions) {
74 FILE* fptr = fopen(filename.c_str(), "r");
75
76 if (fptr == NULL) {
77 LOG(FATAL) << "Error: unable to open file " << filename;
78 return;
79 };
80
81 // This wil die horribly on invalid files. Them's the breaks.
82 FscanfOrDie(fptr, "%d", &num_cameras_);
83 FscanfOrDie(fptr, "%d", &num_points_);
84 FscanfOrDie(fptr, "%d", &num_observations_);
85
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080086 VLOG(1) << "Header: " << num_cameras_ << " " << num_points_ << " "
87 << num_observations_;
Austin Schuh70cc9552019-01-21 19:46:48 -080088
89 point_index_ = new int[num_observations_];
90 camera_index_ = new int[num_observations_];
91 observations_ = new double[2 * num_observations_];
92
93 num_parameters_ = 9 * num_cameras_ + 3 * num_points_;
94 parameters_ = new double[num_parameters_];
95
96 for (int i = 0; i < num_observations_; ++i) {
97 FscanfOrDie(fptr, "%d", camera_index_ + i);
98 FscanfOrDie(fptr, "%d", point_index_ + i);
99 for (int j = 0; j < 2; ++j) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800100 FscanfOrDie(fptr, "%lf", observations_ + 2 * i + j);
Austin Schuh70cc9552019-01-21 19:46:48 -0800101 }
102 }
103
104 for (int i = 0; i < num_parameters_; ++i) {
105 FscanfOrDie(fptr, "%lf", parameters_ + i);
106 }
107
108 fclose(fptr);
109
110 use_quaternions_ = use_quaternions;
111 if (use_quaternions) {
112 // Switch the angle-axis rotations to quaternions.
113 num_parameters_ = 10 * num_cameras_ + 3 * num_points_;
114 double* quaternion_parameters = new double[num_parameters_];
115 double* original_cursor = parameters_;
116 double* quaternion_cursor = quaternion_parameters;
117 for (int i = 0; i < num_cameras_; ++i) {
118 AngleAxisToQuaternion(original_cursor, quaternion_cursor);
119 quaternion_cursor += 4;
120 original_cursor += 3;
121 for (int j = 4; j < 10; ++j) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800122 *quaternion_cursor++ = *original_cursor++;
Austin Schuh70cc9552019-01-21 19:46:48 -0800123 }
124 }
125 // Copy the rest of the points.
126 for (int i = 0; i < 3 * num_points_; ++i) {
127 *quaternion_cursor++ = *original_cursor++;
128 }
129 // Swap in the quaternion parameters.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800130 delete[] parameters_;
Austin Schuh70cc9552019-01-21 19:46:48 -0800131 parameters_ = quaternion_parameters;
132 }
133}
134
135// This function writes the problem to a file in the same format that
136// is read by the constructor.
137void BALProblem::WriteToFile(const std::string& filename) const {
138 FILE* fptr = fopen(filename.c_str(), "w");
139
140 if (fptr == NULL) {
141 LOG(FATAL) << "Error: unable to open file " << filename;
142 return;
143 };
144
145 fprintf(fptr, "%d %d %d\n", num_cameras_, num_points_, num_observations_);
146
147 for (int i = 0; i < num_observations_; ++i) {
148 fprintf(fptr, "%d %d", camera_index_[i], point_index_[i]);
149 for (int j = 0; j < 2; ++j) {
150 fprintf(fptr, " %g", observations_[2 * i + j]);
151 }
152 fprintf(fptr, "\n");
153 }
154
155 for (int i = 0; i < num_cameras(); ++i) {
156 double angleaxis[9];
157 if (use_quaternions_) {
158 // Output in angle-axis format.
159 QuaternionToAngleAxis(parameters_ + 10 * i, angleaxis);
160 memcpy(angleaxis + 3, parameters_ + 10 * i + 4, 6 * sizeof(double));
161 } else {
162 memcpy(angleaxis, parameters_ + 9 * i, 9 * sizeof(double));
163 }
164 for (int j = 0; j < 9; ++j) {
165 fprintf(fptr, "%.16g\n", angleaxis[j]);
166 }
167 }
168
169 const double* points = parameters_ + camera_block_size() * num_cameras_;
170 for (int i = 0; i < num_points(); ++i) {
171 const double* point = points + i * point_block_size();
172 for (int j = 0; j < point_block_size(); ++j) {
173 fprintf(fptr, "%.16g\n", point[j]);
174 }
175 }
176
177 fclose(fptr);
178}
179
180// Write the problem to a PLY file for inspection in Meshlab or CloudCompare.
181void BALProblem::WriteToPLYFile(const std::string& filename) const {
182 std::ofstream of(filename.c_str());
183
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800184 of << "ply" << '\n'
185 << "format ascii 1.0" << '\n'
186 << "element vertex " << num_cameras_ + num_points_ << '\n'
187 << "property float x" << '\n'
188 << "property float y" << '\n'
189 << "property float z" << '\n'
190 << "property uchar red" << '\n'
191 << "property uchar green" << '\n'
192 << "property uchar blue" << '\n'
193 << "end_header" << std::endl;
Austin Schuh70cc9552019-01-21 19:46:48 -0800194
195 // Export extrinsic data (i.e. camera centers) as green points.
196 double angle_axis[3];
197 double center[3];
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800198 for (int i = 0; i < num_cameras(); ++i) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800199 const double* camera = cameras() + camera_block_size() * i;
200 CameraToAngleAxisAndCenter(camera, angle_axis, center);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800201 of << center[0] << ' ' << center[1] << ' ' << center[2] << " 0 255 0"
202 << '\n';
Austin Schuh70cc9552019-01-21 19:46:48 -0800203 }
204
205 // Export the structure (i.e. 3D Points) as white points.
206 const double* points = parameters_ + camera_block_size() * num_cameras_;
207 for (int i = 0; i < num_points(); ++i) {
208 const double* point = points + i * point_block_size();
209 for (int j = 0; j < point_block_size(); ++j) {
210 of << point[j] << ' ';
211 }
212 of << "255 255 255\n";
213 }
214 of.close();
215}
216
217void BALProblem::CameraToAngleAxisAndCenter(const double* camera,
218 double* angle_axis,
219 double* center) const {
220 VectorRef angle_axis_ref(angle_axis, 3);
221 if (use_quaternions_) {
222 QuaternionToAngleAxis(camera, angle_axis);
223 } else {
224 angle_axis_ref = ConstVectorRef(camera, 3);
225 }
226
227 // c = -R't
228 Eigen::VectorXd inverse_rotation = -angle_axis_ref;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800229 AngleAxisRotatePoint(
230 inverse_rotation.data(), camera + camera_block_size() - 6, center);
Austin Schuh70cc9552019-01-21 19:46:48 -0800231 VectorRef(center, 3) *= -1.0;
232}
233
234void BALProblem::AngleAxisAndCenterToCamera(const double* angle_axis,
235 const double* center,
236 double* camera) const {
237 ConstVectorRef angle_axis_ref(angle_axis, 3);
238 if (use_quaternions_) {
239 AngleAxisToQuaternion(angle_axis, camera);
240 } else {
241 VectorRef(camera, 3) = angle_axis_ref;
242 }
243
244 // t = -R * c
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800245 AngleAxisRotatePoint(angle_axis, center, camera + camera_block_size() - 6);
Austin Schuh70cc9552019-01-21 19:46:48 -0800246 VectorRef(camera + camera_block_size() - 6, 3) *= -1.0;
247}
248
Austin Schuh70cc9552019-01-21 19:46:48 -0800249void BALProblem::Normalize() {
250 // Compute the marginal median of the geometry.
251 std::vector<double> tmp(num_points_);
252 Eigen::Vector3d median;
253 double* points = mutable_points();
254 for (int i = 0; i < 3; ++i) {
255 for (int j = 0; j < num_points_; ++j) {
256 tmp[j] = points[3 * j + i];
257 }
258 median(i) = Median(&tmp);
259 }
260
261 for (int i = 0; i < num_points_; ++i) {
262 VectorRef point(points + 3 * i, 3);
263 tmp[i] = (point - median).lpNorm<1>();
264 }
265
266 const double median_absolute_deviation = Median(&tmp);
267
268 // Scale so that the median absolute deviation of the resulting
269 // reconstruction is 100.
270 const double scale = 100.0 / median_absolute_deviation;
271
272 VLOG(2) << "median: " << median.transpose();
273 VLOG(2) << "median absolute deviation: " << median_absolute_deviation;
274 VLOG(2) << "scale: " << scale;
275
276 // X = scale * (X - median)
277 for (int i = 0; i < num_points_; ++i) {
278 VectorRef point(points + 3 * i, 3);
279 point = scale * (point - median);
280 }
281
282 double* cameras = mutable_cameras();
283 double angle_axis[3];
284 double center[3];
285 for (int i = 0; i < num_cameras_; ++i) {
286 double* camera = cameras + camera_block_size() * i;
287 CameraToAngleAxisAndCenter(camera, angle_axis, center);
288 // center = scale * (center - median)
289 VectorRef(center, 3) = scale * (VectorRef(center, 3) - median);
290 AngleAxisAndCenterToCamera(angle_axis, center, camera);
291 }
292}
293
294void BALProblem::Perturb(const double rotation_sigma,
295 const double translation_sigma,
296 const double point_sigma) {
297 CHECK_GE(point_sigma, 0.0);
298 CHECK_GE(rotation_sigma, 0.0);
299 CHECK_GE(translation_sigma, 0.0);
300
301 double* points = mutable_points();
302 if (point_sigma > 0) {
303 for (int i = 0; i < num_points_; ++i) {
304 PerturbPoint3(point_sigma, points + 3 * i);
305 }
306 }
307
308 for (int i = 0; i < num_cameras_; ++i) {
309 double* camera = mutable_cameras() + camera_block_size() * i;
310
311 double angle_axis[3];
312 double center[3];
313 // Perturb in the rotation of the camera in the angle-axis
314 // representation.
315 CameraToAngleAxisAndCenter(camera, angle_axis, center);
316 if (rotation_sigma > 0.0) {
317 PerturbPoint3(rotation_sigma, angle_axis);
318 }
319 AngleAxisAndCenterToCamera(angle_axis, center, camera);
320
321 if (translation_sigma > 0.0) {
322 PerturbPoint3(translation_sigma, camera + camera_block_size() - 6);
323 }
324 }
325}
326
327BALProblem::~BALProblem() {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800328 delete[] point_index_;
329 delete[] camera_index_;
330 delete[] observations_;
331 delete[] parameters_;
Austin Schuh70cc9552019-01-21 19:46:48 -0800332}
333
334} // namespace examples
335} // namespace ceres