Ceres-Solver学习笔记(3)
来源:互联网 发布:好玩的java源代码 编辑:程序博客网 时间:2024/06/06 05:02
Ceres的一个主要目的是解决大尺度bundle adjustment 问题。多说无益,直接上代码:
// file bal_problem.h#ifndef CERES_EXAMPLES_BAL_PROBLEM_H_#define CERES_EXAMPLES_BAL_PROBLEM_H_#include <string>namespace ceres {namespace examples {class BALProblem { public: explicit BALProblem(const std::string& filename, bool use_quaternions); ~BALProblem(); void WriteToFile(const std::string& filename) const; void WriteToPLYFile(const std::string& filename) const; // 将重建的“中心”移到原点,中心是通过计算点的marginal median来确定的。 // 然后对重构进行缩放,以便从原点处得到的点的 median absolute deviation 是100.0 // // 问题的重投影误差保持不变 void Normalize(); // 用特定标准差的随机正态分布值扰乱相机姿态和几何结构 void Perturb(const double rotation_sigma, const double translation_sigma, const double point_sigma); //相机参数的个数 int camera_block_size() const { return use_quaternions_ ? 10 : 9; } // 点参数的个数 int point_block_size() const { return 3; } int num_cameras() const { return num_cameras_; } int num_points() const { return num_points_; } int num_observations() const { return num_observations_; } int num_parameters() const { return num_parameters_; } const int* point_index() const { return point_index_; } const int* camera_index() const { return camera_index_; } const double* observations() const { return observations_; } const double* parameters() const { return parameters_; } const double* cameras() const { return parameters_; } double* mutable_cameras() { return parameters_; } double* mutable_points() { return parameters_ + camera_block_size() * num_cameras_; } private: void CameraToAngleAxisAndCenter(const double* camera, double* angle_axis, double* center) const; void AngleAxisAndCenterToCamera(const double* angle_axis, const double* center, double* camera) const; int num_cameras_; int num_points_; int num_observations_; int num_parameters_; bool use_quaternions_; int* point_index_; int* camera_index_; double* observations_; // The parameter vector is laid out as follows // [camera_1, ..., camera_n, point_1, ..., point_m] double* parameters_;};} // namespace examples} // namespace ceres#endif // CERES_EXAMPLES_BAL_PROBLEM_H_
// file bundle_adjuster.cc#include <algorithm>#include <cmath>#include <cstdio>#include <cstdlib>#include <string>#include <vector>#include "bal_problem.h"#include "ceres/ceres.h"#include "gflags/gflags.h"#include "glog/logging.h"#include "snavely_reprojection_error.h"namespace ceres {namespace examples {void SetLinearSolver(Solver::Options* options) { // 设置LinearSolver,可选项为:"sparse_schur, dense_schur, iterative_schur, // sparse_normal_cholesky, ""dense_qr, dense_normal_cholesky and cgnr." CHECK(StringToLinearSolverType(FLAGS_linear_solver, &options->linear_solver_type)); // 设置PreconditionerType,可选项为:"identity, jacobi, // schur_jacobi, cluster_jacobi, ""cluster_tridiagonal." CHECK(StringToPreconditionerType(FLAGS_preconditioner, &options->preconditioner_type)); // 设置VisibilityClusteringType,可选项为:"single_linkage, // canonical_views" CHECK(StringToVisibilityClusteringType(FLAGS_visibility_clustering, &options->visibility_clustering_type)); // 设置SparseLinearAlgebraLibraryType,可选项为:"suite_sparse, // cx_sparse" CHECK(StringToSparseLinearAlgebraLibraryType( FLAGS_sparse_linear_algebra_library, &options->sparse_linear_algebra_library_type)); // 设置DenseLinearAlgebraLibraryType,可选项为:"eigen, // lapack." CHECK(StringToDenseLinearAlgebraLibraryType( FLAGS_dense_linear_algebra_library, &options->dense_linear_algebra_library_type)); // 线程数 options->num_linear_solver_threads = FLAGS_num_threads; // 是否使用舍尔补 options->use_explicit_schur_complement = FLAGS_explicit_schur_complement;}void SetOrdering(BALProblem* bal_problem, Solver::Options* options) { // 3D点数量 const int num_points = bal_problem->num_points(); // 点的维度(3维) const int point_block_size = bal_problem->point_block_size(); // 点数据起始地址 double* points = bal_problem->mutable_points(); // 相机姿态数 const int num_cameras = bal_problem->num_cameras(); // 相机参数数(9/10) const int camera_block_size = bal_problem->camera_block_size(); // 相机参数起始地址 double* cameras = bal_problem->mutable_cameras(); // true(使用内迭代对非线性进行) // false(细化每个成功的信任区域步骤) if (options->use_inner_iterations) { // 可选参数类型automatic, cameras, points,(points,cameras),(cameras,points) if (FLAGS_blocks_for_inner_iterations == "cameras") { LOG(INFO) << "Camera blocks for inner iterations"; options->inner_iteration_ordering.reset(new ParameterBlockOrdering); for (int i = 0; i < num_cameras; ++i) { // 添加元素到一个组 options->inner_iteration_ordering->AddElementToGroup(cameras + camera_block_size * i, 0); } } else if (FLAGS_blocks_for_inner_iterations == "points") { LOG(INFO) << "Point blocks for inner iterations"; options->inner_iteration_ordering.reset(new ParameterBlockOrdering); for (int i = 0; i < num_points; ++i) { options->inner_iteration_ordering->AddElementToGroup(points + point_block_size * i, 0); } } else if (FLAGS_blocks_for_inner_iterations == "cameras,points") { LOG(INFO) << "Camera followed by point blocks for inner iterations"; options->inner_iteration_ordering.reset(new ParameterBlockOrdering); for (int i = 0; i < num_cameras; ++i) { options->inner_iteration_ordering->AddElementToGroup(cameras + camera_block_size * i, 0); } for (int i = 0; i < num_points; ++i) { options->inner_iteration_ordering->AddElementToGroup(points + point_block_size * i, 1); } } else if (FLAGS_blocks_for_inner_iterations == "points,cameras") { LOG(INFO) << "Point followed by camera blocks for inner iterations"; options->inner_iteration_ordering.reset(new ParameterBlockOrdering); for (int i = 0; i < num_cameras; ++i) { options->inner_iteration_ordering->AddElementToGroup(cameras + camera_block_size * i, 1); } for (int i = 0; i < num_points; ++i) { options->inner_iteration_ordering->AddElementToGroup(points + point_block_size * i, 0); } } else if (FLAGS_blocks_for_inner_iterations == "automatic") { LOG(INFO) << "Choosing automatic blocks for inner iterations"; } else { LOG(FATAL) << "Unknown block type for inner iterations: " << FLAGS_blocks_for_inner_iterations; } } // BA问题有一个稀疏的结构,使它们能够适应更专业、更高效的解决方案策略。斯帕塞舒尔、登斯舒尔和迭代器的解决者利用了这种特殊的结构。 // // 这可以通过指定Options::orderingtype=ceres::SCHUR, // 在这种情况下,Ceres将自动确定正确的参数块排序, // 或者手动指定一个合适的排序向量,定义Options::num_eliminate_blocks。 if (FLAGS_ordering == "automatic") { return; } ceres::ParameterBlockOrdering* ordering = new ceres::ParameterBlockOrdering; // The points come before the cameras. for (int i = 0; i < num_points; ++i) { ordering->AddElementToGroup(points + point_block_size * i, 0); } for (int i = 0; i < num_cameras; ++i) { // When using axis-angle, there is a single parameter block for // the entire camera. ordering->AddElementToGroup(cameras + camera_block_size * i, 1); } options->linear_solver_ordering.reset(ordering);}void SetMinimizerOptions(Solver::Options* options) { // 最大迭代次数 options->max_num_iterations = FLAGS_num_iterations; // 迭代过程是否输出 options->minimizer_progress_to_stdout = true; // 线程数 options->num_threads = FLAGS_num_threads; // 每次迭代的精度 options->eta = FLAGS_eta; // 求解时间 options->max_solver_time_in_seconds = FLAGS_max_solver_time; // 信任区间算法/nonmonotic options->use_nonmonotonic_steps = FLAGS_nonmonotonic_steps; if (FLAGS_line_search) { options->minimizer_type = ceres::LINE_SEARCH; } CHECK(StringToTrustRegionStrategyType(FLAGS_trust_region_strategy, &options->trust_region_strategy_type)); // 可选项:raditional_dogleg,subspace_dogleg CHECK(StringToDoglegType(FLAGS_dogleg, &options->dogleg_type)); options->use_inner_iterations = FLAGS_inner_iterations;}void SetSolverOptionsFromFlags(BALProblem* bal_problem, Solver::Options* options) { SetMinimizerOptions(options); SetLinearSolver(options); SetOrdering(bal_problem, options);}void BuildProblem(BALProblem* bal_problem, Problem* problem) { const int point_block_size = bal_problem->point_block_size(); const int camera_block_size = bal_problem->camera_block_size(); double* points = bal_problem->mutable_points(); double* cameras = bal_problem->mutable_cameras(); // Observations是特征的坐标 u v const double* observations = bal_problem->observations(); for (int i = 0; i < bal_problem->num_observations(); ++i) { CostFunction* cost_function; // Each Residual block takes a point and a camera as input and // outputs a 2 dimensional residual. // 加入约束 特征位置 cost_function = (FLAGS_use_quaternions) ? SnavelyReprojectionErrorWithQuaternions::Create( observations[2 * i + 0], observations[2 * i + 1]) : SnavelyReprojectionError::Create( observations[2 * i + 0], observations[2 * i + 1]); // If enabled use Huber's loss function. LossFunction* loss_function = FLAGS_robustify ? new HuberLoss(1.0) : NULL; // 没一个特征对应这一个相机姿态和一个三维点 // 加入点 和 相机 double* camera = cameras + camera_block_size * bal_problem->camera_index()[i]; double* point = points + point_block_size * bal_problem->point_index()[i]; problem->AddResidualBlock(cost_function, loss_function, camera, point); } if (FLAGS_use_quaternions && FLAGS_use_local_parameterization) { LocalParameterization* camera_parameterization = new ProductParameterization( new QuaternionParameterization(), new IdentityParameterization(6)); for (int i = 0; i < bal_problem->num_cameras(); ++i) { // ???? problem->SetParameterization(cameras + camera_block_size * i, camera_parameterization); } }}void SolveProblem(const char* filename) { // 实例化BALProblem 导入文件信息 BALProblem bal_problem(filename, FLAGS_use_quaternions); if (!FLAGS_initial_ply.empty()) { bal_problem.WriteToPLYFile(FLAGS_initial_ply); } Problem problem; srand(FLAGS_random_seed); bal_problem.Normalize(); // 添加噪声 bal_problem.Perturb(FLAGS_rotation_sigma, FLAGS_translation_sigma, FLAGS_point_sigma); // 添加约束 BuildProblem(&bal_problem, &problem); Solver::Options options; // 设置优化选项 SetSolverOptionsFromFlags(&bal_problem, &options); options.gradient_tolerance = 1e-16; options.function_tolerance = 1e-16; Solver::Summary summary; //求解 Solve(options, &problem, &summary); std::cout << summary.FullReport() << "\n"; if (!FLAGS_final_ply.empty()) { bal_problem.WriteToPLYFile(FLAGS_final_ply); }}} // namespace examples} // mainint main(int argc, char** argv) { CERES_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); if (FLAGS_input.empty()) { LOG(ERROR) << "Usage: bundle_adjuster --input=bal_problem"; return 1; } CHECK(FLAGS_use_quaternions || !FLAGS_use_local_parameterization) << "--use_local_parameterization can only be used with " << "--use_quaternions."; ceres::examples::SolveProblem(FLAGS_input.c_str()); return 0;}
// file snavely_reprojection_error.h#include "ceres/rotation.h"namespace ceres {namespace examples {// 小孔相机模型. camera用9个参数表示, 3 个表示旋转, 3 个表示平移, 1个焦距,2个径向畸变// 中心点没有建模,假设为图片中心struct SnavelyReprojectionError { SnavelyReprojectionError(double observed_x, double observed_y) : observed_x(observed_x), observed_y(observed_y) {} template <typename T> bool operator()(const T* const camera, const T* const point, T* residuals) const { // camera[0,1,2] 是angle-axis 旋转. T p[3]; AngleAxisRotatePoint(camera, point, p); // camera[3,4,5] are the translation. // 点转换到相机坐标系 p[0] += camera[3]; p[1] += camera[4]; p[2] += camera[5]; // Compute the center of distortion. The sign change comes from // the camera model that Noah Snavely's Bundler assumes, whereby // the camera coordinate system has a negative z axis. const T xp = - p[0] / p[2]; const T yp = - p[1] / p[2]; // Apply second and fourth order radial distortion. const T& l1 = camera[7]; const T& l2 = camera[8]; const T r2 = xp*xp + yp*yp; const T distortion = T(1.0) + r2 * (l1 + l2 * r2); // Compute final projected point position. const T& focal = camera[6]; // 得到3D点投影到图像上的位置 const T predicted_x = focal * distortion * xp; const T predicted_y = focal * distortion * yp; // 特征位置与3D点计算位置的差 residuals[0] = predicted_x - T(observed_x); residuals[1] = predicted_y - T(observed_y); return true; } // Factory to hide the construction of the CostFunction object from // the client code. static ceres::CostFunction* Create(const double observed_x, const double observed_y) { // 使用AutoDiffCostFunction,2个残差,参数1有9维,参数2有3维 return (new ceres::AutoDiffCostFunction<SnavelyReprojectionError, 2, 9, 3>( new SnavelyReprojectionError(observed_x, observed_y))); } double observed_x; double observed_y;};// Templated pinhole camera model for used with Ceres. The camera is// parameterized using 10 parameters. 4 for rotation, 3 for// translation, 1 for focal length and 2 for radial distortion. The// principal point is not modeled (i.e. it is assumed be located at// the image center).struct SnavelyReprojectionErrorWithQuaternions { // (u, v): the position of the observation with respect to the image // center point. SnavelyReprojectionErrorWithQuaternions(double observed_x, double observed_y) : observed_x(observed_x), observed_y(observed_y) {} template <typename T> bool operator()(const T* const camera, const T* const point, T* residuals) const { // camera[0,1,2,3] is are the rotation of the camera as a quaternion. // // We use QuaternionRotatePoint as it does not assume that the // quaternion is normalized, since one of the ways to run the // bundle adjuster is to let Ceres optimize all 4 quaternion // parameters without a local parameterization. T p[3]; QuaternionRotatePoint(camera, point, p); p[0] += camera[4]; p[1] += camera[5]; p[2] += camera[6]; // Compute the center of distortion. The sign change comes from // the camera model that Noah Snavely's Bundler assumes, whereby // the camera coordinate system has a negative z axis. const T xp = - p[0] / p[2]; const T yp = - p[1] / p[2]; // Apply second and fourth order radial distortion. const T& l1 = camera[8]; const T& l2 = camera[9]; const T r2 = xp*xp + yp*yp; const T distortion = T(1.0) + r2 * (l1 + l2 * r2); // Compute final projected point position. const T& focal = camera[7]; const T predicted_x = focal * distortion * xp; const T predicted_y = focal * distortion * yp; // The error is the difference between the predicted and observed position. residuals[0] = predicted_x - T(observed_x); residuals[1] = predicted_y - T(observed_y); return true; } // Factory to hide the construction of the CostFunction object from // the client code. static ceres::CostFunction* Create(const double observed_x, const double observed_y) { return (new ceres::AutoDiffCostFunction< SnavelyReprojectionErrorWithQuaternions, 2, 10, 3>( new SnavelyReprojectionErrorWithQuaternions(observed_x, observed_y))); } double observed_x; double observed_y;};
这个例程比较长,看起来很复杂,但是实际上只是将options的一些选项,列举了出来,具体的使用也没有很好的介绍,本质上并没有什么太难的。options的可设置参数非常之多,具体可以看solver.h文件。
阅读全文
0 0
- Ceres-Solver学习笔记(3)
- ceres solver 学习笔记
- Ceres-Solver学习笔记(1)
- Ceres-Solver学习笔记(2)
- Ceres-Solver学习笔记(4)
- Ceres-Solver学习笔记(5)
- Ceres-Solver学习笔记(6)
- Ceres-Solver学习笔记(7)
- Ceres-Solver学习笔记(8)
- Ceres-Solver学习笔记(9)
- Ceres-Solver学习笔记(1)
- Ceres-Solver学习笔记(2)
- Ceres solver
- Ceres-Solver库入门
- Ceres solver tutorial
- Windows 配置 Ceres-solver
- VS2013 ceres-solver编译
- ceres-solver拟合椭球
- Unity3D Vector3.Dot 点乘 与 Vector3.Cross 叉乘
- C++学习笔记之数组
- hdu3072 Intelligence System【强连通】
- jQuery添加节点___例_添加表格
- PAT 1069微博转发抽奖
- Ceres-Solver学习笔记(3)
- 暑期集训之喷水装置一
- 【BZOJ】4554 [Tjoi2016&Heoi2016]游戏 二分图
- NYOJ 144 小柯的烦恼
- ssh三大框架整合时struts2遇到的简单又很容易忽视的问题
- main函数参数_可变参数
- HDU 2017多校联赛-1012 Balala Power!
- 程序设计思维
- 给时间定一个目标