ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

openMVG 内嵌的 Ceres Solver:从非线性最小二乘建模到大规模 Bundle Adjustment 实战指南

openMVG 内嵌的 Ceres Solver:从非线性最小二乘建模到大规模 Bundle Adjustment 实战指南 计算机视觉科研【免费下载链接】openMVGopen Multiple View Geometry library. Basis for 3D computer vision and Structure from Motion.项目地址https://gitcode.com/gh_mirrors/op/openMVG点击查看免费下载Ceres Solver 是 openMVG 仓库内嵌的通用非线性最小二乘优化引擎当前仓库内版本为 1.13.0见 VERSION在 openMVG 中承担 Bundle Adjustment光束法平差这一核心计算任务。本文以仓库内 Ceres Solver 官方文档首页 index.rst 为主线系统梳理 Ceres 的特性、安装配置、建模与求解流程、三种求导方式并结合 openMVG 的 sfm_data_BA_ceres.cpp 源码带你理解 Ceres 在真实三维重建管线中如何落地。读完本文你将掌握 Ceres 从定义一个代价函数到跑通大规模光束法平差的完整技术路径。一、Ceres Solver 是什么文档首页给出的定位根据官方文档首页的描述Ceres Solver 是一个开源的 C 库用于建模并求解大规模、复杂的优化问题具体能力包括求解带边界约束的**非线性最小二乘Non-linear Least Squares**问题求解一般无约束优化问题自 2010 年起在 Google 生产环境中使用是一个成熟、功能丰富、性能优异的库。文档同时给出两个社区入口讨论与提问邮件列表ceres-solvergooglegroups.com以及用于提交 bug 报告与功能请求的 GitHub Issue Tracker。这些信息表明 Ceres 拥有活跃的维护生态这也是 openMVG 选择将其作为内嵌第三方依赖的原因之一。文档首页还以 toctree 的形式组织了整套文档的章节导航本文后续各节将逐一展开这些章节的核心内容章节主题features特性总览installation安装与构建tutorial教程非线性最小二乘 梯度求解derivatives三种求导方式详解nnls_modeling非线性最小二乘建模 APInnls_solving求解器配置与选择nnls_covariance协方差估计gradient_solver梯度下降求解器faqs常见问题users用户列表version_history版本历史license许可协议BSD命名渊源Ceres谷神星得名于天文学史上的一次里程碑事件——高斯仅用 22 次对刚发现的小行星 Ceres 的观测就通过最小二乘法准确预测了它从太阳背后重新出现的时间和位置见文档首页脚注。Ceres 因此成为最小二乘这一数学方法的象征详情可读 bibliography。二、特性总览为什么 openMVG 选择 Ceresfeatures.rst 列出了 Ceres 的核心特性这些特性恰好覆盖了三维重建SfM中对优化引擎的苛刻需求1. 建模 API逐项累加的目标函数现实中几乎不存在一次就能写对完整目标函数的情况。Ceres 的建模 API 允许用户一个残差项一个残差项地构建和修改目标函数并且无需担心每次修改对底层问题稀疏结构的影响——求解器会自动适应。2. 求导自动、数值、解析三种方式自由混用提供导数是最繁琐、最易出错的工作。Ceres 内置自动微分Automatic Differentiation与数值微分Numeric Differentiation除非你确实想手算否则不必手写 Jacobian。更重要的是Ceres 允许在同一问题中任意混合自动、数值与解析导数。3. 鲁棒损失函数Robust Loss Functions有数据就有离群点。Ceres 允许通过LossFunction对残差进行塑形降低离群值对解的影响。这正是 openMVG 的 BA 代码中使用HuberLoss的动机见下文源码分析。4. 局部参数化Local Parameterization许多参数并不生活在欧氏空间中例如旋转矩阵。用户可以通过LocalParameterization对象指定局部切空间的几何结构。openMVG 的 BA 中用SubsetParameterization/SubsetManifold固定部分相机参数正是这一特性的应用。5. 求解器选择Trust Region 与 Line Search 两大体系根据问题规模、稀疏结构、时间与内存预算的不同Ceres 提供多种优化算法Trust Region 求解器支持 Levenberg-Marquardt、Powells Dogleg、Subspace Dogleg其核心计算代价是求解一个线性系统为此 Ceres 提供多种线性求解器——稠密问题的稠密 QR 与稠密 Cholesky 分解基于 Eigen 或 LAPACK、大规模稀疏问题的稀疏 Cholesky 分解基于 SuiteSparse、CXSparse 或 Eigen以及针对Bundle Adjustment问题特化的基于 Schur 补的稠密、稀疏与迭代线性求解器。Line Search 求解器当问题规模大到无法存储和分解 Jacobian、或需要低成本的低精度解时提供非线性共轭梯度、BFGS、LBFGS 等多种变体。6. 性能、质量与可移植性Ceres 经过大量优化C 模板化、手写线性代数例程、基于 OpenMP 的 Jacobian 求值与线性求解多线程。文档声称 Ceres 在 NIST 基准问题集上表现最佳该表述为官方文档陈述原文见 features 章节。此外它支持协方差估计、可运行于 Linux/Windows/macOS/Android/iOS并采用宽松的 BSD 许可便于随应用分发。三、安装与构建从依赖到 CMake 集成installation.rst 是完整的安装指南以下内容与当前仓库1.13.0 版本对应。1. 获取源码官方提供两种途径下载最新稳定版 tarball或克隆 git 仓库。在 openMVG 仓库中Ceres 已被直接内嵌为第三方依赖位于 src/third_party/ceres-solver并带有 openMVG 特有的构建说明 README.openMVG因此使用 openMVG 时通常无需单独获取 Ceres 源码。2. 依赖清单依赖版本要求说明Eigen必需3.1.0强烈推荐3.2.2也可用作稀疏线性代数库对应EIGENSPARSE选项CMake2.8.0除 Android 外所有平台必需构建系统glog0.3.1推荐详细日志可用MINIGLOG选项启用内置的最小替代实现gflags—构建示例与测试需要SuiteSparse可选大规模 BA强烈推荐求解大型稀疏线性系统CXSparse可选更简单但更慢不依赖 LAPACK/BLASBLAS/LAPACKSuiteSparse 需要Ceres 也直接使用UNIX 上推荐 ATLAS 或 OpenBLAS需关闭 OpenBLAS 内部线程macOS 自动使用 Accelerate 框架3. Linux以 Ubuntu 为例构建流程# 安装依赖 sudo apt-get install cmake sudo apt-get install libgoogle-glog-dev sudo apt-get install libatlas-base-dev sudo apt-get install libeigen3-dev sudo apt-get install libsuitesparse-dev # 可选静态库构建可用系统包 # 构建、测试、安装 tar zxf ceres-solver-1.13.0.tar.gz mkdir ceres-bin cd ceres-bin cmake ../ceres-solver-1.13.0 make -j3 make test make install构建完成后可以用内置的 BAL 数据集问题验证安装bin/simple_bundle_adjuster ../ceres-solver-1.13.0/data/problem-16-22106-pre.txt该命令以DENSE_SCHUR线性求解器运行最多 10 次迭代。官方文档给出了完整的迭代日志示例其中关键信息包括参数块 22122 个、参数 66462 个、残差块 83718 个、残差 167436 个最终代价从4.185660e06收敛到1.803390e04终止原因为函数容差收敛|cost_change|/cost: 1.769766e-09 1.000000e-06。这正是 openMVG 中大规模 BA 运行的典型形态。4. 其他平台要点macOSsudo port install ceres-solverMacPorts或brew install ceres-solverHomebrew需启用homebrew/sciencetapBLAS/LAPACK 由系统 vecLib 框架提供无需单独安装。Windows支持 Visual Studio 2010但功能与测试完备度低于 Linux/macOSSuiteSparse/CXSparse 无官方构建支持需在 CMake GUI 中设置EIGEN_INCLUDE_DIR_HINTS、GLOG_INCLUDE_DIR_HINTS、GLOG_LIBRARY_DIR_HINTS、GFLAGS_INCLUDE_DIR_HINTS、GFLAGS_LIBRARY_DIR_HINTS以及可选的 SuiteSparse/CXSparse 相关变量注意windows.h若启用 GDI其ERROR宏会与 glog/miniglog 的ERROR日志级别冲突可用GLOG_NO_ABBREVIATED_SEVERITIES或预定义NOGDI解决Windows 上支持的求解器为DENSE_QR、DENSE_SCHUR、CGNR、ITERATIVE_SCHUR。Android下载 NDK r9d 及以上在jni目录运行ndk-build使用生成的libceres.a。iOSiOS 7.0通过-DCMAKE_TOOLCHAIN_FILE../ceres-solver/cmake/iOS.cmake强制使用 iOS 工具链IOS_PLATFORM可取OS/SIMULATOR/SIMULATOR64再用lipo合并默认配置为仅依赖 Eigen 的精简版本无SPARSE_SCHUR等适合中小规模问题。5. 自定义构建选项CMake 配置项官方文档强调这些选项应通过 CMake GUI 或命令行-DOPTIONON/OFF设置且注意-D传参会在每次 configure 时强制覆盖缓存变量即使你在ccmake界面中修改也会被覆盖。核心选项如下选项默认值含义LAPACKON关闭后同时禁用SUITESPARSE其依赖 LAPACKSUITESPARSEON关闭以构建不带 SuiteSparse 的 CeresCXSPARSEON关闭以构建不带 CXSparse 的 CeresEIGENSPARSEOFFEigen 稀疏 Cholesky 代码为 LGPL 许可开启会使 Ceres 整体变为 LGPL建议 Eigen 3.2.2GFLAGSON关闭后部分示例代码将无法构建MINIGLOGOFF用内置的精简 glog 替代外部 glogSCHUR_SPECIALIZATIONSON关闭可减小二进制体积/编译时间但SPARSE_SCHUR损失约 10–20% 性能OPENMPONAndroid 等平台不支持时关闭多线程CXX11OFF非 MSVC控制shared_ptr/unordered_map使用 TR1 还是 C11 版本开启后要求所有客户端代码以-stdc11编译BUILD_SHARED_LIBSOFF默认静态库开启则构建共享库EXPORT_BUILD_DIROFF将构建目录导出到 CMake 用户包注册表无需安装即可被find_package(Ceres)发现BUILD_DOCUMENTATIONOFF构建文档需要 Sphinx 与 sphinx-better-themeMSVC_USE_STATIC_CRTOFF仅 Windows使用静态 C 运行时库LIB_SUFFIX64非 Debian/Arch 的 64 位 Linux库安装目录后缀${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}依赖位置可通过两类变量指定NAME_INCLUDE_DIR_HINTS/NAME_LIBRARY_DIR_HINTS搜索提示目录或NAME_INCLUDE_DIR/NAME_LIBRARY显式指定文件绕过搜索但保留校验。BLAS/LAPACK 找不到时可设置CMAKE_LIBRARY_PATH或通过CMAKE_MODULE_PATH提供自定义FindBLAS.cmake/FindLAPACK.cmake需提供与默认脚本一致的完整变量集。6. 在客户端项目中使用 CeresCMake 集成使用find_package()的前提是Ceres 已被make install安装或开启了EXPORT_BUILD_DIR导出了构建目录。最小示例cmake_minimum_required(VERSION 2.8) project(helloworld) find_package(Ceres REQUIRED) include_directories(${CERES_INCLUDE_DIRS}) add_executable(helloworld helloworld.cc) target_link_libraries(helloworld ${CERES_LIBRARIES})组件COMPONENTS过滤可指定LAPACK、SuiteSparse、CXSparse、EigenSparse、SparseLinearAlgebraLibrary三者任一、SchurSpecializations、OpenMP、C11等组件例如# 要求检测到同时编译了 SuiteSparse 与 EigenSparse 的 Ceres find_package(Ceres REQUIRED COMPONENTS SuiteSparse EigenSparse)版本指定find_package(Ceres 1.2.3 REQUIRED)可附加版本检查多版本共存时用Ceres_DIR指定具体安装/导出位置非标准安装路径则用find_package(Ceres REQUIRED PATHS /some/where/local/)。理解 CMake 包系统安装 Ceres 时会同时安装CeresConfig.cmake定义CERES_INCLUDE_DIRS与CERES_LIBRARIES变量与自动生成的CeresTargets.cmake定义imported target。关键区别CERES_LIBRARIES的值ceres是 imported target 的名字它封装了 Ceres 的全部公开链接依赖如果直接链接libceres.a/so/dylib而绕过 imported target会因缺少这些依赖导致链接错误。另外注意imported target 不会在依赖它的项目再次导出时被重新导出——如果你的项目Foo使用了 Ceres 且Foo自身被导出另一个项目Bar在find_package(Foo)后将看不到ceres这个 imported target需要在FooConfig.cmake中主动调用find_package(Ceres)CMake 2.8 风格用HINTS ${Ceres_DIR}传递路径CMake 3.x 风格用find_dependency(Ceres)转发 REQUIRED/QUIET 参数。四、非线性最小二乘教程从 Hello World 到 Bundle Adjustmenttutorial.rst 将教程分为 nnls_tutorial非线性最小二乘与 gradient_tutorial梯度求解两章。以下沿 nnls 教程的主线展开。1. 问题的一般形式Ceres 求解的是带边界约束的鲁棒非线性最小二乘问题$$\min_{\mathbf{x}} \frac{1}{2}\sum_{i} \rho_i\left(\left|f_i\left(x_{i_1}, ... ,x_{i_k}\right)\right|^2\right) \quad \text{s.t.} \quad l_j \le x_j \le u_j$$其中表达式 $\rho_i(|f_i(\cdot)|^2)$ 称为一个ResidualBlock残差块$f_i(\cdot)$ 是一个CostFunction代价函数依赖若干参数块一小群标量如平移向量三分量、四元数四分量组成一个ParameterBlock参数块单个标量也可自成一块$l_j$、$u_j$ 是参数块 $x_j$ 的上下界$\rho_i$ 是LossFunction损失函数用于降低离群点的影响特例当 $\rho_i(x) x$恒等且 $l_j-\infty$、$u_j\infty$ 时退化为熟悉的普通非线性最小二乘问题。这类问题横跨科学与工程多个领域——从统计学中的曲线拟合到计算机视觉中从照片构建三维模型即 Bundle Adjustment。2. Hello World第一个 Ceres 程序以求 $\frac{1}{2}(10-x)^2$ 的最小值为例最优解 $x10$。第一步是编写求值 $f(x)10-x$ 的仿函数struct CostFunctor { template typename T bool operator()(const T* const x, T* residual) const { residual[0] T(10.0) - x[0]; return true; } };关键点operator()是模板方法所有输入输出都是类型T。Ceres 需要残差值时以Tdouble调用需要 Jacobian 时以特殊类型TJet调用——这就是自动微分的实现机制。然后构建问题并求解int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); double initial_x 5.0; double x initial_x; Problem problem; // 用自动微分获得导数Jacobian CostFunction* cost_function new AutoDiffCostFunctionCostFunctor, 1, 1(new CostFunctor); problem.AddResidualBlock(cost_function, NULL, x); // 运行求解器 Solver::Options options; options.linear_solver_type ceres::DENSE_QR; options.minimizer_progress_to_stdout true; Solver::Summary summary; Solve(options, problem, summary); std::cout summary.BriefReport() \n; std::cout x : initial_x - x \n; return 0; }AutoDiffCostFunction接收CostFunctor自动对其求导并包装成CostFunction接口。AutoDiffCostFunctionCostFunctor, 1, 1的三个模板参数依次是残差维度、参数块个数1 个与每个参数块维度1 维。运行输出iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time 0 4.512500e01 0.00e00 9.50e00 0.00e00 0.00e00 1.00e04 0 5.33e-04 3.46e-03 1 4.511598e-07 4.51e01 9.50e-04 9.50e00 1.00e00 3.00e04 1 5.00e-04 4.05e-03 2 5.012552e-16 4.51e-07 3.17e-08 9.50e-04 1.00e00 9.00e04 1 1.60e-05 4.09e-03 Ceres Solver Report: Iterations: 2, Initial cost: 4.512500e01, Final cost: 5.012552e-16, Termination: CONVERGENCE x : 0.5 - 10从 $x5$ 出发两次迭代即收敛到 10。严格说求解器实际跑了三次迭代第三次因参数更新量过小而判定收敛Ceres 只在迭代结束时打印且检测到收敛立即终止。值得注意的是这是线性问题理论上一次线性求解即可默认求解器配置面向非线性问题且第一轮迭代就已非常接近最优值 0。3. 三种求导方式教程在 Derivatives 一节对比了三种求导方式更完整的论述见 derivatives.rst数值微分Numeric Derivatives当无法编写模板化仿函数例如残差计算调用了不受控的库函数时使用。仿函数无需模板化struct NumericDiffCostFunctor { bool operator()(const double* const x, double* residual) const { residual[0] 10.0 - x[0]; return true; } };加入问题时代码与自动微分几乎一致仅多一个指示有限差分格式的模板参数CostFunction* cost_function new NumericDiffCostFunctionNumericDiffCostFunctor, ceres::CENTRAL, 1, 1( new NumericDiffCostFunctor); problem.AddResidualBlock(cost_function, NULL, x);官方明确建议优先用自动微分而非数值微分——C 模板使自动微分高效而数值微分昂贵、易引入数值误差且收敛更慢。解析导数Analytic Derivatives当闭式导数比链式法则更高效时可继承CostFunction或编译期已知尺寸的SizedCostFunction自行实现残差与 Jacobianclass QuadraticCostFunction : public ceres::SizedCostFunction1, 1 { public: virtual ~QuadraticCostFunction() {} virtual bool Evaluate(double const* const* parameters, double* residuals, double** jacobians) const { const double x parameters[0][0]; residuals[0] 10 - x; if (jacobians ! NULL jacobians[0] ! NULL) { jacobians[0][0] -1; } return true; } };注意Evaluate必须检查jacobians是否为非空再填充导数。实现CostFunction较为繁琐官方建议除非有充分理由自行管理 Jacobian否则使用AutoDiffCostFunction或NumericDiffCostFunction。自动微分Automatic Derivatives基于 C 模板与运算符重载自动计算解析导数即 Hello World 中使用的方式。进阶求导工具还包括DynamicAutoDiffCostFunction参数块数量或尺寸编译期未知时使用、CostFunctionToFunctor、NumericDiffFunctor、ConditionedCostFunction等。文档首页的总体建议是优先自动微分某些场景值得手写解析导数数值微分只作为最后手段主要用于对接外部库。4. Powell 函数多残差块示例Powell 函数最小化展示了如何将目标函数拆成多个只依赖部分参数的残差块。定义 $x[x_1,x_2,x_3,x_4]$四个残差为$$f_1(x) x_1 10x_2,\quad f_2(x) \sqrt{5}(x_3-x_4),\quad f_3(x) (x_2-2x_3)^2,\quad f_4(x) \sqrt{10}(x_1-x_4)^2$$以 $f_4$ 为例定义仿函数struct F4 { template typename T bool operator()(const T* const x1, const T* const x4, T* residual) const { residual[0] T(sqrt(10.0)) * (x1[0] - x4[0]) * (x1[0] - x4[0]); return true; } };问题构建的关键是每个残差块只依赖其对应的两个参数而非全部四个double x1 3.0; double x2 -1.0; double x3 0.0; double x4 1.0; Problem problem; problem.AddResidualBlock(new AutoDiffCostFunctionF1, 1, 1, 1(new F1), NULL, x1, x2); problem.AddResidualBlock(new AutoDiffCostFunctionF2, 1, 1, 1(new F2), NULL, x3, x4); problem.AddResidualBlock(new AutoDiffCostFunctionF3, 1, 1, 1(new F3), NULL, x2, x3); problem.AddResidualBlock(new AutoDiffCostFunctionF4, 1, 1, 1(new F4), NULL, x1, x4);运行 14 次迭代后代价从1.075000e02降至1.791438e-14终止条件为梯度容差收敛Gradient max norm: 3.642190e-11 1.000000e-10最终 $x_10.000292189, x_2-2.92189e-05, x_34.79511e-05, x_44.79511e-05$——在 10 次迭代内代价已降到 $4\times10^{-12}$ 量级。5. 曲线拟合带数据的非线性最小二乘此前示例都是无数据的纯优化问题。曲线拟合示例的数据来自对 $y e^{0.3x0.1}$ 采样并叠加标准差 $\sigma0.2$ 的高斯噪声目标是拟合 $y e^{mxc}$。每个观测对应一个残差struct ExponentialResidual { ExponentialResidual(double x, double y) : x_(x), y_(y) {} template typename T bool operator()(const T* const m, const T* const c, T* residual) const { residual[0] T(y_) - exp(m[0] * T(x_) c[0]); return true; } private: const double x_; const double y_; };问题构建即每个观测加一个代价函数double m 0.0; double c 0.0; Problem problem; for (int i 0; i kNumObservations; i) { CostFunction* cost_function new AutoDiffCostFunctionExponentialResidual, 1, 1, 1( new ExponentialResidual(data[2 * i], data[2 * i 1])); problem.AddResidualBlock(cost_function, NULL, m, c); }从 $m0, c0$、初始代价 121.173 出发13 次迭代后收敛到 $m0.291861, c0.131439$、代价 1.056751。该结果与真实参数 $m0.3, c0.1$ 有偏差是符合预期的——从含噪数据重建曲线本就会偏离真值若用真实参数求值代价反而更差1.082425。下图展示了拟合效果。6. 鲁棒曲线拟合用 LossFunction 对抗离群点当数据中存在不服从噪声模型的离群点时普通最小二乘拟合的曲线会明显偏离真值。标准对策是引入LossFunction降低高残差残差块通常对应离群点的影响。用法只需在AddResidualBlock的第二参数传入损失函数// 之前problem.AddResidualBlock(cost_function, NULL, m, c); problem.AddResidualBlock(cost_function, new CauchyLoss(0.5), m, c);CauchyLoss是 Ceres 内置损失函数之一参数0.5指定损失函数的尺度。加入后拟合曲线明显回归真值附近7. Bundle AdjustmentCeres 的主战场Ceres 诞生的主要动机之一正是求解大规模 Bundle AdjustmentBA问题给定一组图像特征点位置与对应关系寻找使重投影误差最小的 3D 点位置与相机参数。误差通常定义为观测特征位置与对应 3D 点在相机成像平面投影之差的平方 $L_2$ 范数。教程使用华盛顿大学 BAL 数据集中的一个问题。每个残差依赖一个三维点与一个九参数相机3 个 Rodrigues 轴角旋转 3 个平移 1 个焦距 2 个径向畸变。重投影残差仿函数如下这是 openMVG BA 代码的直接原型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]; ceres::AngleAxisRotatePoint(camera, point, p); // camera[3,4,5] 是平移 p[0] camera[3]; p[1] camera[4]; p[2] camera[5]; // 计算畸变中心符号变化来自 Bundler 相机模型相机坐标系 z 轴为负 T xp - p[0] / p[2]; T yp - p[1] / p[2]; // 应用二阶与四阶径向畸变 const T l1 camera[7]; const T l2 camera[8]; T r2 xp*xp yp*yp; T distortion T(1.0) r2 * (l1 l2 * r2); // 计算最终投影点位置 const T focal camera[6]; T predicted_x focal * distortion * xp; T predicted_y focal * distortion * yp; // 误差 预测位置与观测位置之差 residuals[0] predicted_x - T(observed_x); residuals[1] predicted_y - T(observed_y); return true; } static ceres::CostFunction* Create(const double observed_x, const double observed_y) { return (new ceres::AutoDiffCostFunctionSnavelyReprojectionError, 2, 9, 3( new SnavelyReprojectionError(observed_x, observed_y))); } double observed_x; double observed_y; };AngleAxisRotatePoint等旋转操作函数位于include/ceres/rotation.h仓库内对应 include/ceres/rotation.h。注意该残差是非平凡函数手写解析 Jacobian 相当痛苦——自动微分让这一切变得简单。问题构建同样遵循每个观测加一个残差块的模式ceres::Problem problem; for (int i 0; i bal_problem.num_observations(); i) { ceres::CostFunction* cost_function SnavelyReprojectionError::Create( bal_problem.observations()[2 * i 0], bal_problem.observations()[2 * i 1]); problem.AddResidualBlock(cost_function, NULL /* squared loss */, bal_problem.mutable_camera_for_observation(i), bal_problem.mutable_point_for_observation(i)); }BA 问题规模大且具有特殊稀疏结构。教程指出虽然设置SPARSE_NORMAL_CHOLESKY是合理做法但 BA 的稀疏结构可以被更高效地利用——Ceres 为此提供三个特化求解器统称Schur-based solvers示例使用最简单的DENSE_SCHURceres::Solver::Options options; options.linear_solver_type ceres::DENSE_SCHUR; options.minimizer_progress_to_stdout true; ceres::Solver::Summary summary; ceres::Solve(options, problem, summary); std::cout summary.FullReport() \n;8. 其他示例索引教程还列举了 examples 目录下的其他完整示例bundle_adjuster.cc综合演示多种线性求解器、鲁棒损失函数与局部参数化、circle_fit.cc圆拟合、ellipse_approximation.cc演示Solver::Options::dynamic_sparsity、denoising.cc基于 Fields of Experts 模型的图像去噪、nist.ccNIST 非线性回归基准、more_garbow_hillstrom.ccMore-Garbow-Hillstrom 测试问题、libmv_bundle_adjuster.ccBlender/libmv 使用的 BA 算法、libmv_homography.cc单应求解与自定义退出准则、robot_pose_mle.cc演示DynamicAutoDiffCostFunction的机器人位姿 MLE、slam/pose_graph_2d与slam/pose_graph_3d2D/3D 位姿图优化g2o 格式输入输出poses_original.txt/poses_optimized.txt配套plot_results.py可视化脚本。五、导数专题如何做出明智选择derivatives.rst 指出定义目标函数及其 Jacobian 是使用 Ceres 求解优化问题时的首要任务正确且高效地计算 Jacobian 是良好性能的关键。Ceres 提供三种途径解析导数用户自己手工或借助 Maple/Mathematica 等工具推导并在CostFunction中实现数值导数Ceres 用有限差分计算自动导数Ceres 用 C 模板与运算符重载自动计算解析导数。三种方式可单独或组合使用。给没耐心的人的高层建议是用自动导数某些场景值得用解析导数避免数值导数只作为最后手段主要用于对接外部库。该章节还包含spivak_notationSpivak 记号、analytical_derivatives、numerical_derivatives、automatic_derivatives、interfacing_with_autodiff五个子章节分别深入推导导数计算的数学细节与实现机制适合需要精确控制导数质量的进阶读者。六、openMVG 中的 Ceres源码级实证openMVG 的 BA 模块是 Ceres 在仓库中的核心消费方实现位于 sfm_data_BA_ceres.cpp 与 sfm_data_BA_ceres.hpp。从源码可以清晰看到官方文档中各项特性的落地求解器与线性求解器自动选择openMVG 的BA_options默认linear_solver_type_ ceres::DENSE_SCHUR、preconditioner_type_ ceres::JACOBI随后根据ceres::IsSparseLinearAlgebraLibraryTypeAvailable(...)的探测结果自动升级到SPARSE_SCHUR优先SUITE_SPARSE其次EIGEN_SPARSE。这与文档中BA 问题应使用 Schur 特化求解器的建议完全一致。鲁棒损失函数默认使用ceres::HuberLoss(Square(4.0))即尺度为 4 的 Huber 损失对应 4 像素级别的重投影误差容忍并通过problem_options.loss_function_ownership ceres::DO_NOT_TAKE_OWNERSHIP管理所有权——这正是文档所述用 LossFunction 降低离群点影响的工程实践。局部参数化对每个相机位姿用ceres::SubsetParameterization(6, vec_constant_extrinsic)或ceres::SubsetManifold(6, vec_constant_extrinsic)固定不优化部分外参分量实现部分参数冻结的局部切空间建模。自动微分代价函数相机内参、外参、点坐标等残差块均通过ceres::AutoDiffCostFunction...构建例如PoseCenterConstraintCostFunction的AutoDiffCostFunctionPoseCenterConstraintCostFunction, 3, 6并使用ceres::RotationMatrixToAngleAxis/ceres::AngleAxisToRotationMatrix在旋转矩阵与轴角表示之间转换——与教程中AngleAxisRotatePoint的用法一脉相承。求解执行通过ceres::Solver::Options配置并设置logging_type ceres::SILENT抑制日志调用ceres::Solve(ceres_config_options, problem, summary)完成 BA随后从ceres::Solver::Summary提取结果写回 openMVG 的sfm_data结构。这一调用链完整对应了官方文档的建模流程Problem逐块添加残差 →Solver::Options配置求解器 →Solve求解 →Summary输出报告。如果你想在 openMVG 中替换或调优 BA 行为sfm_data_BA_ceres.cpp 就是最直接的入手点。七、引用、许可与进一步阅读若在出版物中使用了 Ceres Solver文档首页要求按如下方式引用misc{ceres-solver, author Sameer Agarwal and Keir Mierle and Others, title Ceres Solver, howpublished \url{http://ceres-solver.org}, }Ceres 采用 BSD 许可详见 license.rst这使它可以随 openMVG 等应用自由分发。进一步阅读路径建模 API 细节nnls_modeling.rst代价函数、损失函数、局部参数化、问题构建的完整类参考求解器配置与调参nnls_solving.rst线性求解器、预条件器、信任域策略、迭代选项、终止条件解的不确定性分析nnls_covariance.rst协方差估计梯度下降求解器gradient_solver.rst常见问题faqs.rst版本演进version_history.rst贡献指南contributing.rst用户列表users.rst。在 openMVG 的实际使用中你几乎不需要直接与 Ceres 的安装细节打交道——仓库已将其内嵌为 src/third_party/ceres-solver 下的第三方依赖由 openMVG 的顶层 CMake 构建系统统一编译。理解本文所述的建模与求解原理能帮助你在使用 openMVG 的sfm_data_BA_ceres接口或阅读其源码时准确把握每个配置项背后的优化机制。赞分享计算机视觉科研【免费下载链接】openMVGopen Multiple View Geometry library. Basis for 3D computer vision and Structure from Motion.项目地址https://gitcode.com/gh_mirrors/op/openMVG点击查看免费下载相关推荐openMVG 中的 Ceres Solver 非线性最小二乘求解实战指南openMVG 中的 Ceres Solver 非线性最小二乘求解实战指南 导读 本教程源自 Ceres Solver 官方文档 nnls_tutorial.r计算机视觉科研Ceres Solver 协方差估计Covariance Estimation实战指南基于 openMVG 内嵌 Ceres 的非线性最小二乘解不确定性分析Ceres Solver 协方差估计Covariance Estimation实战指南基于 openMVG 内嵌 Ceres 的非线性最小二乘解不确定性分计算机视觉科研CANN opbase 数据格式转换指南ToOpFormat 将 aclFormat 转为 op::Format 的完整解析CANN opbase 数据格式转换指南ToOpFormat 将 aclFormat 转为 op::Format 的完整解析 导读 ToOpFormat 是计算机视觉科研上一篇SeaTunnel Transform Recipes 实战指南嵌套 JSON 解析、MySQL 向量化与 Milvus 入库下一篇如何用Claudian插件实现Obsidian笔记的自动备份策略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表