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