1 // Ceres Solver - A fast non-linear least squares minimizer 2 // Copyright 2010, 2011, 2012 Google Inc. All rights reserved. 3 // http://code.google.com/p/ceres-solver/ 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 (at) google.com (Sameer Agarwal) 30 31 #include "bal_problem.h" 32 33 #include <cstdio> 34 #include <cstdlib> 35 #include <string> 36 #include <vector> 37 #include "Eigen/Core" 38 #include "ceres/rotation.h" 39 #include "glog/logging.h" 40 41 namespace ceres { 42 namespace examples { 43 namespace { 44 typedef Eigen::Map<Eigen::VectorXd> VectorRef; 45 typedef Eigen::Map<const Eigen::VectorXd> ConstVectorRef; 46 47 inline double RandDouble() { 48 double r = static_cast<double>(rand()); 49 return r / RAND_MAX; 50 } 51 52 // Box-Muller algorithm for normal random number generation. 53 // http://en.wikipedia.org/wiki/Box-Muller_transform 54 inline double RandNormal() { 55 double x1, x2, w; 56 do { 57 x1 = 2.0 * RandDouble() - 1.0; 58 x2 = 2.0 * RandDouble() - 1.0; 59 w = x1 * x1 + x2 * x2; 60 } while ( w >= 1.0 || w == 0.0 ); 61 62 w = sqrt((-2.0 * log(w)) / w); 63 return x1 * w; 64 } 65 66 template<typename T> 67 void FscanfOrDie(FILE* fptr, const char* format, T* value) { 68 int num_scanned = fscanf(fptr, format, value); 69 if (num_scanned != 1) { 70 LOG(FATAL) << "Invalid UW data file."; 71 } 72 } 73 74 void PerturbPoint3(const double sigma, double* point) { 75 for (int i = 0; i < 3; ++i) { 76 point[i] += RandNormal() * sigma; 77 } 78 } 79 80 double Median(std::vector<double>* data) { 81 int n = data->size(); 82 std::vector<double>::iterator mid_point = data->begin() + n / 2; 83 std::nth_element(data->begin(), mid_point, data->end()); 84 return *mid_point; 85 } 86 87 } // namespace 88 89 BALProblem::BALProblem(const std::string& filename, bool use_quaternions) { 90 FILE* fptr = fopen(filename.c_str(), "r"); 91 92 if (fptr == NULL) { 93 LOG(FATAL) << "Error: unable to open file " << filename; 94 return; 95 }; 96 97 // This wil die horribly on invalid files. Them's the breaks. 98 FscanfOrDie(fptr, "%d", &num_cameras_); 99 FscanfOrDie(fptr, "%d", &num_points_); 100 FscanfOrDie(fptr, "%d", &num_observations_); 101 102 VLOG(1) << "Header: " << num_cameras_ 103 << " " << num_points_ 104 << " " << num_observations_; 105 106 point_index_ = new int[num_observations_]; 107 camera_index_ = new int[num_observations_]; 108 observations_ = new double[2 * num_observations_]; 109 110 num_parameters_ = 9 * num_cameras_ + 3 * num_points_; 111 parameters_ = new double[num_parameters_]; 112 113 for (int i = 0; i < num_observations_; ++i) { 114 FscanfOrDie(fptr, "%d", camera_index_ + i); 115 FscanfOrDie(fptr, "%d", point_index_ + i); 116 for (int j = 0; j < 2; ++j) { 117 FscanfOrDie(fptr, "%lf", observations_ + 2*i + j); 118 } 119 } 120 121 for (int i = 0; i < num_parameters_; ++i) { 122 FscanfOrDie(fptr, "%lf", parameters_ + i); 123 } 124 125 fclose(fptr); 126 127 use_quaternions_ = use_quaternions; 128 if (use_quaternions) { 129 // Switch the angle-axis rotations to quaternions. 130 num_parameters_ = 10 * num_cameras_ + 3 * num_points_; 131 double* quaternion_parameters = new double[num_parameters_]; 132 double* original_cursor = parameters_; 133 double* quaternion_cursor = quaternion_parameters; 134 for (int i = 0; i < num_cameras_; ++i) { 135 AngleAxisToQuaternion(original_cursor, quaternion_cursor); 136 quaternion_cursor += 4; 137 original_cursor += 3; 138 for (int j = 4; j < 10; ++j) { 139 *quaternion_cursor++ = *original_cursor++; 140 } 141 } 142 // Copy the rest of the points. 143 for (int i = 0; i < 3 * num_points_; ++i) { 144 *quaternion_cursor++ = *original_cursor++; 145 } 146 // Swap in the quaternion parameters. 147 delete []parameters_; 148 parameters_ = quaternion_parameters; 149 } 150 } 151 152 // This function writes the problem to a file in the same format that 153 // is read by the constructor. 154 void BALProblem::WriteToFile(const std::string& filename) const { 155 FILE* fptr = fopen(filename.c_str(), "w"); 156 157 if (fptr == NULL) { 158 LOG(FATAL) << "Error: unable to open file " << filename; 159 return; 160 }; 161 162 fprintf(fptr, "%d %d %d\n", num_cameras_, num_points_, num_observations_); 163 164 for (int i = 0; i < num_observations_; ++i) { 165 fprintf(fptr, "%d %d", camera_index_[i], point_index_[i]); 166 for (int j = 0; j < 2; ++j) { 167 fprintf(fptr, " %g", observations_[2 * i + j]); 168 } 169 fprintf(fptr, "\n"); 170 } 171 172 for (int i = 0; i < num_cameras(); ++i) { 173 double angleaxis[9]; 174 if (use_quaternions_) { 175 // Output in angle-axis format. 176 QuaternionToAngleAxis(parameters_ + 10 * i, angleaxis); 177 memcpy(angleaxis + 3, parameters_ + 10 * i + 4, 6 * sizeof(double)); 178 } else { 179 memcpy(angleaxis, parameters_ + 9 * i, 9 * sizeof(double)); 180 } 181 for (int j = 0; j < 9; ++j) { 182 fprintf(fptr, "%.16g\n", angleaxis[j]); 183 } 184 } 185 186 const double* points = parameters_ + camera_block_size() * num_cameras_; 187 for (int i = 0; i < num_points(); ++i) { 188 const double* point = points + i * point_block_size(); 189 for (int j = 0; j < point_block_size(); ++j) { 190 fprintf(fptr, "%.16g\n", point[j]); 191 } 192 } 193 194 fclose(fptr); 195 } 196 197 void BALProblem::CameraToAngleAxisAndCenter(const double* camera, 198 double* angle_axis, 199 double* center) { 200 VectorRef angle_axis_ref(angle_axis, 3); 201 if (use_quaternions_) { 202 QuaternionToAngleAxis(camera, angle_axis); 203 } else { 204 angle_axis_ref = ConstVectorRef(camera, 3); 205 } 206 207 // c = -R't 208 Eigen::VectorXd inverse_rotation = -angle_axis_ref; 209 AngleAxisRotatePoint(inverse_rotation.data(), 210 camera + camera_block_size() - 6, 211 center); 212 VectorRef(center, 3) *= -1.0; 213 } 214 215 void BALProblem::AngleAxisAndCenterToCamera(const double* angle_axis, 216 const double* center, 217 double* camera) { 218 ConstVectorRef angle_axis_ref(angle_axis, 3); 219 if (use_quaternions_) { 220 AngleAxisToQuaternion(angle_axis, camera); 221 } else { 222 VectorRef(camera, 3) = angle_axis_ref; 223 } 224 225 // t = -R * c 226 AngleAxisRotatePoint(angle_axis, 227 center, 228 camera + camera_block_size() - 6); 229 VectorRef(camera + camera_block_size() - 6, 3) *= -1.0; 230 } 231 232 233 void BALProblem::Normalize() { 234 // Compute the marginal median of the geometry. 235 std::vector<double> tmp(num_points_); 236 Eigen::Vector3d median; 237 double* points = mutable_points(); 238 for (int i = 0; i < 3; ++i) { 239 for (int j = 0; j < num_points_; ++j) { 240 tmp[j] = points[3 * j + i]; 241 } 242 median(i) = Median(&tmp); 243 } 244 245 for (int i = 0; i < num_points_; ++i) { 246 VectorRef point(points + 3 * i, 3); 247 tmp[i] = (point - median).lpNorm<1>(); 248 } 249 250 const double median_absolute_deviation = Median(&tmp); 251 252 // Scale so that the median absolute deviation of the resulting 253 // reconstruction is 100. 254 const double scale = 100.0 / median_absolute_deviation; 255 256 VLOG(2) << "median: " << median.transpose(); 257 VLOG(2) << "median absolute deviation: " << median_absolute_deviation; 258 VLOG(2) << "scale: " << scale; 259 260 // X = scale * (X - median) 261 for (int i = 0; i < num_points_; ++i) { 262 VectorRef point(points + 3 * i, 3); 263 point = scale * (point - median); 264 } 265 266 double* cameras = mutable_cameras(); 267 double angle_axis[3]; 268 double center[3]; 269 for (int i = 0; i < num_cameras_; ++i) { 270 double* camera = cameras + camera_block_size() * i; 271 CameraToAngleAxisAndCenter(camera, angle_axis, center); 272 // center = scale * (center - median) 273 VectorRef(center, 3) = scale * (VectorRef(center, 3) - median); 274 AngleAxisAndCenterToCamera(angle_axis, center, camera); 275 } 276 } 277 278 void BALProblem::Perturb(const double rotation_sigma, 279 const double translation_sigma, 280 const double point_sigma) { 281 CHECK_GE(point_sigma, 0.0); 282 CHECK_GE(rotation_sigma, 0.0); 283 CHECK_GE(translation_sigma, 0.0); 284 285 double* points = mutable_points(); 286 if (point_sigma > 0) { 287 for (int i = 0; i < num_points_; ++i) { 288 PerturbPoint3(point_sigma, points + 3 * i); 289 } 290 } 291 292 for (int i = 0; i < num_cameras_; ++i) { 293 double* camera = mutable_cameras() + camera_block_size() * i; 294 295 double angle_axis[3]; 296 double center[3]; 297 // Perturb in the rotation of the camera in the angle-axis 298 // representation. 299 CameraToAngleAxisAndCenter(camera, angle_axis, center); 300 if (rotation_sigma > 0.0) { 301 PerturbPoint3(rotation_sigma, angle_axis); 302 } 303 AngleAxisAndCenterToCamera(angle_axis, center, camera); 304 305 if (translation_sigma > 0.0) { 306 PerturbPoint3(translation_sigma, camera + camera_block_size() - 6); 307 } 308 } 309 } 310 311 BALProblem::~BALProblem() { 312 delete []point_index_; 313 delete []camera_index_; 314 delete []observations_; 315 delete []parameters_; 316 } 317 318 } // namespace examples 319 } // namespace ceres 320