bundle_adjustment_test_util.h 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Ceres Solver - A fast non-linear least squares minimizer
  2. // Copyright 2023 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: keir@google.com (Keir Mierle)
  30. //
  31. // End-to-end bundle adjustment test utilities for Ceres. This base is used in
  32. // the generated bundle adjustment test binaries. The reason to split the
  33. // bundle tests into separate binaries is so the tests can get parallelized.
  34. #include <cmath>
  35. #include <cstdio>
  36. #include <cstdlib>
  37. #include <string>
  38. #include "ceres/autodiff_cost_function.h"
  39. #include "ceres/internal/export.h"
  40. #include "ceres/ordered_groups.h"
  41. #include "ceres/problem.h"
  42. #include "ceres/rotation.h"
  43. #include "ceres/solver.h"
  44. #include "ceres/stringprintf.h"
  45. #include "ceres/test_util.h"
  46. #include "ceres/types.h"
  47. #include "glog/logging.h"
  48. namespace ceres {
  49. namespace internal {
  50. const bool kAutomaticOrdering = true;
  51. const bool kUserOrdering = false;
  52. // This class implements the SystemTestProblem interface and provides
  53. // access to a bundle adjustment problem. It is based on
  54. // examples/bundle_adjustment_example.cc. Currently a small 16 camera
  55. // problem is hard coded in the constructor.
  56. class BundleAdjustmentProblem {
  57. public:
  58. BundleAdjustmentProblem(const std::string input_file) {
  59. ReadData(input_file);
  60. BuildProblem();
  61. }
  62. BundleAdjustmentProblem() {
  63. const std::string input_file =
  64. TestFileAbsolutePath("problem-16-22106-pre.txt");
  65. ReadData(input_file);
  66. BuildProblem();
  67. }
  68. ~BundleAdjustmentProblem() {
  69. delete[] point_index_;
  70. delete[] camera_index_;
  71. delete[] observations_;
  72. delete[] parameters_;
  73. }
  74. Problem* mutable_problem() { return &problem_; }
  75. Solver::Options* mutable_solver_options() { return &options_; }
  76. // clang-format off
  77. int num_cameras() const { return num_cameras_; }
  78. int num_points() const { return num_points_; }
  79. int num_observations() const { return num_observations_; }
  80. const int* point_index() const { return point_index_; }
  81. const int* camera_index() const { return camera_index_; }
  82. const double* observations() const { return observations_; }
  83. double* mutable_cameras() { return parameters_; }
  84. double* mutable_points() { return parameters_ + 9 * num_cameras_; }
  85. const Solver::Options& options() const { return options_; }
  86. // clang-format on
  87. static double kResidualTolerance;
  88. private:
  89. void ReadData(const std::string& filename) {
  90. FILE* fptr = fopen(filename.c_str(), "r");
  91. if (!fptr) {
  92. LOG(FATAL) << "File Error: unable to open file " << filename;
  93. }
  94. // This will die horribly on invalid files. Them's the breaks.
  95. FscanfOrDie(fptr, "%d", &num_cameras_);
  96. FscanfOrDie(fptr, "%d", &num_points_);
  97. FscanfOrDie(fptr, "%d", &num_observations_);
  98. VLOG(1) << "Header: " << num_cameras_ << " " << num_points_ << " "
  99. << num_observations_;
  100. point_index_ = new int[num_observations_];
  101. camera_index_ = new int[num_observations_];
  102. observations_ = new double[2 * num_observations_];
  103. num_parameters_ = 9 * num_cameras_ + 3 * num_points_;
  104. parameters_ = new double[num_parameters_];
  105. for (int i = 0; i < num_observations_; ++i) {
  106. FscanfOrDie(fptr, "%d", camera_index_ + i);
  107. FscanfOrDie(fptr, "%d", point_index_ + i);
  108. for (int j = 0; j < 2; ++j) {
  109. FscanfOrDie(fptr, "%lf", observations_ + 2 * i + j);
  110. }
  111. }
  112. for (int i = 0; i < num_parameters_; ++i) {
  113. FscanfOrDie(fptr, "%lf", parameters_ + i);
  114. }
  115. fclose(fptr);
  116. }
  117. void BuildProblem() {
  118. double* points = mutable_points();
  119. double* cameras = mutable_cameras();
  120. for (int i = 0; i < num_observations(); ++i) {
  121. // Each Residual block takes a point and a camera as input and
  122. // outputs a 2 dimensional residual.
  123. CostFunction* cost_function =
  124. new AutoDiffCostFunction<BundlerResidual, 2, 9, 3>(
  125. new BundlerResidual(observations_[2 * i + 0],
  126. observations_[2 * i + 1]));
  127. // Each observation corresponds to a pair of a camera and a point
  128. // which are identified by camera_index()[i] and
  129. // point_index()[i] respectively.
  130. double* camera = cameras + 9 * camera_index_[i];
  131. double* point = points + 3 * point_index()[i];
  132. problem_.AddResidualBlock(cost_function, nullptr, camera, point);
  133. }
  134. options_.linear_solver_ordering =
  135. std::make_shared<ParameterBlockOrdering>();
  136. // The points come before the cameras.
  137. for (int i = 0; i < num_points_; ++i) {
  138. options_.linear_solver_ordering->AddElementToGroup(points + 3 * i, 0);
  139. }
  140. for (int i = 0; i < num_cameras_; ++i) {
  141. options_.linear_solver_ordering->AddElementToGroup(cameras + 9 * i, 1);
  142. }
  143. options_.linear_solver_type = DENSE_SCHUR;
  144. options_.max_num_iterations = 25;
  145. options_.function_tolerance = 1e-10;
  146. options_.gradient_tolerance = 1e-10;
  147. options_.parameter_tolerance = 1e-10;
  148. }
  149. template <typename T>
  150. void FscanfOrDie(FILE* fptr, const char* format, T* value) {
  151. int num_scanned = fscanf(fptr, format, value);
  152. if (num_scanned != 1) {
  153. LOG(FATAL) << "Invalid UW data file.";
  154. }
  155. }
  156. // Templated pinhole camera model. The camera is parameterized
  157. // using 9 parameters. 3 for rotation, 3 for translation, 1 for
  158. // focal length and 2 for radial distortion. The principal point is
  159. // not modeled (i.e. it is assumed to be located at the image
  160. // center).
  161. struct BundlerResidual {
  162. // (u, v): the position of the observation with respect to the image
  163. // center point.
  164. BundlerResidual(double u, double v) : u(u), v(v) {}
  165. template <typename T>
  166. bool operator()(const T* const camera,
  167. const T* const point,
  168. T* residuals) const {
  169. T p[3];
  170. AngleAxisRotatePoint(camera, point, p);
  171. // Add the translation vector
  172. p[0] += camera[3];
  173. p[1] += camera[4];
  174. p[2] += camera[5];
  175. const T& focal = camera[6];
  176. const T& l1 = camera[7];
  177. const T& l2 = camera[8];
  178. // Compute the center of distortion. The sign change comes from
  179. // the camera model that Noah Snavely's Bundler assumes, whereby
  180. // the camera coordinate system has a negative z axis.
  181. T xp = -focal * p[0] / p[2];
  182. T yp = -focal * p[1] / p[2];
  183. // Apply second and fourth order radial distortion.
  184. T r2 = xp * xp + yp * yp;
  185. T distortion = T(1.0) + r2 * (l1 + l2 * r2);
  186. residuals[0] = distortion * xp - u;
  187. residuals[1] = distortion * yp - v;
  188. return true;
  189. }
  190. double u;
  191. double v;
  192. };
  193. Problem problem_;
  194. Solver::Options options_;
  195. int num_cameras_;
  196. int num_points_;
  197. int num_observations_;
  198. int num_parameters_;
  199. int* point_index_;
  200. int* camera_index_;
  201. double* observations_;
  202. // The parameter vector is laid out as follows
  203. // [camera_1, ..., camera_n, point_1, ..., point_m]
  204. double* parameters_;
  205. };
  206. double BundleAdjustmentProblem::kResidualTolerance = 1e-4;
  207. using BundleAdjustmentTest = SystemTest<BundleAdjustmentProblem>;
  208. } // namespace internal
  209. } // namespace ceres