bal_problem.cc 11 KB

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