3D视觉工坊和计算机视觉工坊优先发表自己文章(博客优先级滞后)

bundle adjustment 的历史发展

bundle adjustment,中文名称是光束法平差,经典的BA目的是优化相机的pose和landmark,其在SfM和SLAM 领域中扮演者重要角色.目前大多数书籍或者参老文献将其翻译成"捆绑调整"是不太严谨的做法.bundle adjustment 最早是19世纪由搞大地测量学(测绘学科)的人提出来的,19世纪中期的时候,geodetics的学者就开始研究large scale triangulations(大型三角剖分)。20世纪中期,随着camera和computer的出现,photogrammetry(摄影测量学)也开始研究adjustment computation,所以他们给起了个名字叫bundle adjustment(隶属摄影测量学科前辈的功劳)。21世纪前后,robotics领域开始兴起SLAM,最早用的recursive bayesian filter(递归贝叶斯滤波),后来把问题搞成个graph然后用least squares方法求解,bundle adjusment历史发展图如下:

bundle adjustment 其本质还是离不开最小二乘原理(Gauss功劳)(几乎所有优化问题其本质都是最小二乘),目前bundle adjustment 优化框架最为代表的是ceres solver和g2o(这里主要介绍ceres solver).据说ceres的命名是天文学家Piazzi闲暇无事的时候观测一颗没有观测到的星星,最后用least squares算出了这个小行星的轨道,故将这个小行星命名为ceres.

Bundle adjustment 的算法理论

观测值:像点坐标
优化量(平差量):pose 和landmark
因为一旦涉及平差,就必定有如下公式:观测值+观测值改正数=近似值+近似值改正数,那么bundle adjustment 的公式还是从共线条件方程出发:

x−x0=−fa1(XA−XS)+b1(YA−YS)+c1(ZA−LS)a3(XA−XS)+b3(YA−YS)+c3(ZA−ZS)y−y0=−fa2(XA−XS)+b2(YA−YS)+c2(ZA−ZS)a3(XA−XS)+b3(YA−YS)+c3(ZA−ZS)\begin{array}{l} x-x_{0}=-f \frac{a_{1}\left(X_{A}-X_{S}\right)+b_{1}\left(Y_{A}-Y_{S}\right)+c_{1}\left(Z_{A}-L_{S}\right)}{a_{3}\left(X_{A}-X_{S}\right)+b_{3}\left(Y_{A}-Y_{S}\right)+c_{3}\left(Z_{A}-Z_{S}\right)} \\ y-y_{0}=-f \frac{a_{2}\left(X_{A}-X_{S}\right)+b_{2}\left(Y_{A}-Y_{S}\right)+c_{2}\left(Z_{A}-Z_{S}\right)}{a_{3}\left(X_{A}-X_{S}\right)+b_{3}\left(Y_{A}-Y_{S}\right)+c_{3}\left(Z_{A}-Z_{S}\right)} \end{array} x−x0​=−fa3​(XA​−XS​)+b3​(YA​−YS​)+c3​(ZA​−ZS​)a1​(XA​−XS​)+b1​(YA​−YS​)+c1​(ZA​−LS​)​y−y0​=−fa3​(XA​−XS​)+b3​(YA​−YS​)+c3​(ZA​−ZS​)a2​(XA​−XS​)+b2​(YA​−YS​)+c2​(ZA​−ZS​)​​

优化函数(误差方程)如下,其中uij是像点坐标,Cj是相机投影矩阵,Xi是三维点坐标:

min⁡∑i=1n∑j=1m(uij−π(Cj,Xi))2\min \sum_{i=1}^{n} \sum_{j=1}^{m}\left(u_{i j}-\pi\left(C_{j}, X_{i}\right)\right)^{2} mini=1∑n​j=1∑m​(uij​−π(Cj​,Xi​))2

四种Bundle adjustment 算法代码

这里代码主要从四个方面来介绍:

  • 优化相机内参及畸变系数,相机的pose(6dof)和landmark
    代价函数写法如下:
template <typename CameraModel>
class BundleAdjustmentCostFunction {public:explicit BundleAdjustmentCostFunction(const Eigen::Vector2d& point2D): observed_x_(point2D(0)), observed_y_(point2D(1)) {}
//构造函数传入的是观测值static ceres::CostFunction* Create(const Eigen::Vector2d& point2D) {return (new ceres::AutoDiffCostFunction<BundleAdjustmentCostFunction<CameraModel>, 2, 4, 3, 3,CameraModel::kNumParams>(new BundleAdjustmentCostFunction(point2D)));}
//优化量:2代表误差方程个数;4代表pose中的姿态信息,用四元数表示;3代表pose中的位置信息;3代表landmark
自由度;CameraModel::kNumParams是相机内参和畸变系数,其取决于相机模型是what// opertator 重载函数的参数即是待优化的量template <typename T>bool operator()(const T* const qvec, const T* const tvec,const T* const point3D, const T* const camera_params,T* residuals) const {// Rotate and translate.T projection[3];ceres::UnitQuaternionRotatePoint(qvec, point3D, projection);projection[0] += tvec[0];projection[1] += tvec[1];projection[2] += tvec[2];// Project to image plane.projection[0] /= projection[2];projection[1] /= projection[2];// Distort and transform to pixel space.CameraModel::WorldToImage(camera_params, projection[0], projection[1],&residuals[0], &residuals[1]);// Re-projection error.residuals[0] -= T(observed_x_);residuals[1] -= T(observed_y_);return true;}private:const double observed_x_;const double observed_y_;
};

写好了代价函数,下面就是需要把参数都加入残差块,让ceres自动求导,代码如下:

ceres::Problem problem;
ceres::CostFunction* cost_function = nullptr;
ceres::LossFunction * p_LossFunction =ceres_options_.bUse_loss_function_ ?new ceres::HuberLoss(Square(4.0)): nullptr; // 关于为何使用损失函数,因为现实中并不是所有观测过程中的噪声都服从 //gaussian noise的(或者可以说几乎没有),//遇到有outlier的情况,这些方法非常容易挂掉,//这时候就得用到robust statistics里面的//robust cost(*cost也可以叫做loss, 统计学那边喜欢叫risk) function了,//比较常用的有huber, cauchy等等。
cost_function = BundleAdjustmentCostFunction<CameraModel>::Create(point2D.XY());
//将优化量加入残差块
problem_->AddResidualBlock(cost_function, p_LossFunction, qvec_data,tvec_data, point3D.XYZ().data(),camera_params_data);

如上,case1 的bundle adjustment 就搭建完成!

  • 优化相机内参及畸变系数,pose subset parameterization(pose 信息部分参数优化)和3D landmark,
    只优化姿态信息时候
    ,problem需要添加的代码如下:
    //这里姿态又用欧拉角表示map_poses[indexPose] = {angleAxis[0], angleAxis[1], angleAxis[2], t(0), t(1), t(2)};double * parameter_block = &map_poses.at(indexPose)[0];problem.AddParameterBlock(parameter_block, 6);std::vector<int> vec_constant_extrinsic;vec_constant_extrinsic.insert(vec_constant_extrinsic.end(), {3,4,5});if (!vec_constant_extrinsic.empty()){// 主要用到ceres的SubsetParameterization函数ceres::SubsetParameterization *subset_parameterization =new ceres::SubsetParameterization(6, vec_constant_extrinsic);problem.SetParameterization(parameter_block, subset_parameterization);} 
  • 优化相机内参及畸变系数,pose subset parameterization(pose 信息部分参数优化)和3D landmark,
    只优化位置信息时候
    ,problem需要添加的代码如下(同上面代码,只需修改一行):
    //这里姿态又用欧拉角表示map_poses[indexPose] = {angleAxis[0], angleAxis[1], angleAxis[2], t(0), t(1), t(2)};double * parameter_block = &map_poses.at(indexPose)[0];problem.AddParameterBlock(parameter_block, 6);std::vector<int> vec_constant_extrinsic;vec_constant_extrinsic.insert(vec_constant_extrinsic.end(), {1,2,3});if (!vec_constant_extrinsic.empty()){ceres::SubsetParameterization *subset_parameterization =new ceres::SubsetParameterization(6, vec_constant_extrinsic);problem.SetParameterization(parameter_block, subset_parameterization);} 
  • 优化相机内参及畸变系数,pose 是常量不优化 和3D landmark.
    代价函数写法如下:
//相机模型
template <typename CameraModel>
class BundleAdjustmentConstantPoseCostFunction {public:BundleAdjustmentConstantPoseCostFunction(const Eigen::Vector4d& qvec,const Eigen::Vector3d& tvec,const Eigen::Vector2d& point2D): qw_(qvec(0)),qx_(qvec(1)),qy_(qvec(2)),qz_(qvec(3)),tx_(tvec(0)),ty_(tvec(1)),tz_(tvec(2)),observed_x_(point2D(0)),observed_y_(point2D(1)) {}static ceres::CostFunction* Create(const Eigen::Vector4d& qvec,const Eigen::Vector3d& tvec,const Eigen::Vector2d& point2D) {return (new ceres::AutoDiffCostFunction<BundleAdjustmentConstantPoseCostFunction<CameraModel>, 2, 3,CameraModel::kNumParams>(new BundleAdjustmentConstantPoseCostFunction(qvec, tvec, point2D)));}template <typename T>bool operator()(const T* const point3D, const T* const camera_params,T* residuals) const {const T qvec[4] = {T(qw_), T(qx_), T(qy_), T(qz_)};// Rotate and translate.T projection[3];ceres::UnitQuaternionRotatePoint(qvec, point3D, projection);projection[0] += T(tx_);projection[1] += T(ty_);projection[2] += T(tz_);// Project to image plane.projection[0] /= projection[2];projection[1] /= projection[2];// Distort and transform to pixel space.CameraModel::WorldToImage(camera_params, projection[0], projection[1],&residuals[0], &residuals[1]);// Re-projection error.residuals[0] -= T(observed_x_);residuals[1] -= T(observed_y_);return true;}private:const double qw_;const double qx_;const double qy_;const double qz_;const double tx_;const double ty_;const double tz_;const double observed_x_;const double observed_y_;
};

接下来problem 加入残差块代码如下:

ceres::Problem problem;
ceres::CostFunction* cost_function = nullptr;
ceres::LossFunction * p_LossFunction =ceres_options_.bUse_loss_function_ ?new ceres::HuberLoss(Square(4.0)): nullptr; // 关于为何使用损失函数,因为现实中并不是所有观测过程中的噪声都服从 //gaussian noise的(或者可以说几乎没有),//遇到有outlier的情况,这些方法非常容易挂掉,//这时候就得用到robust statistics里面的//robust cost(*cost也可以叫做loss, 统计学那边喜欢叫risk) function了,//比较常用的有huber, cauchy等等。
cost_function = BundleAdjustmentConstantPoseCostFunction<CameraModel>::Create( \image.Qvec(), image.Tvec(), point2D.XY());//观测值输入
//将优化量加入残差块problem_->AddResidualBlock(cost_function, loss_function, \point3D.XYZ().data(), camera_params_data);//被优化量加入残差-3D点和相机内参

以上就是四种BA 的case 当然还可以有很多变种,比如gps约束的BA(即是附有限制条件的间接平差),比如
固定3D landmark,优化pose和相机参数和畸变系数

参考资料

  • colmap openmvg 源代码,github 地址:

    https://github.com/openMVG/openMVG

    https://github.com/colmap/colmap

  • 单杰. 光束法平差简史与概要. 武汉大学学报·信息科学版, 2018, 43(12): 1797-1810.

bundle adjustment 详解相关推荐

  1. 【Google Play】App Bundle 使用详解 ( 应用模块化 )

    Google Play 上架完整流程 系列文章目录 [Google Play]创建 Google 开发者账号 ( 注册邮箱账号 | 创建开发者账号 ) [Google Play]创建并设置应用 ( 访 ...

  2. 【Google Play】App Bundle 使用详解 ( 简介 | 应用内更新 | 即时更新 | 灵活更新 )

    Google Play 上架完整流程 系列文章目录 [Google Play]创建 Google 开发者账号 ( 注册邮箱账号 | 创建开发者账号 ) [Google Play]创建并设置应用 ( 访 ...

  3. android bundle 机制,【Android开发】Bundle机制详解

    在同一个地方跌倒两次,才能体会到"好记性不如烂笔头"! 一.Bundle简介 bundle在Android开发中非常常见,它的作用主要时用于传递数据:它所保存的数据是以key-va ...

  4. 史上最简SLAM零基础解读(7) - Jacobian matrix(雅可比矩阵) → 理论分析与应用详解(Bundle Adjustment)

    本人讲解关于slam一系列文章汇总链接:史上最全slam从零开始   文末正下方中心提供了本人联系方式,点击本人照片即可显示WX→官方认证{\color{blue}{文末正下方中心}提供了本人 \co ...

  5. Bundle Adjustment 光束法平差详解

    转自http://blog.csdn.net/junshen1314/article/details/48860951 首先引述来自维基百科的定义:假设我们有一个3D空间中的点,他被位于不同位置的多个 ...

  6. 第五章 MVC之Bundle详解

    一.简述 Bundle,英文原意就是捆.收集.归拢.在MVC中的Bundle技术,也就是一个对css和js文件的捆绑压缩的技术. 它的用处: 将多个请求捆绑为一个请求,减少服务器请求数 压缩javas ...

  7. react native bundle读取assets_react-native-easy-app 详解与使用之 (一)AsyncStorage

    react-native-easy-app 是一款为React Native App快速开发提供基础服务的纯JS库(支持 IOS & Android),特别是在从0到1的项目搭建初期,至少可以 ...

  8. ORB-SLAM2代码/流程详解

    ORB-SLAM2代码详解 文章目录 ORB-SLAM2代码详解 1. ORB-SLAM2代码详解01_ORB-SLAM2代码运行流程 1 运行官方Demo 1.2. 阅读代码之前你应该知道的事情 1 ...

  9. dso详解--dso原理

    本篇文章摘自:高翔的最新的知乎文章,转载请注明原文链接,同时感谢翔哥(半闲居士)一直以来对国内vslam做出的贡献. 原文地址: https://zhuanlan.zhihu.com/p/291775 ...

  10. PnP算法详解(超详细公式推导)

    PnP算法详解 PnP概述 PnP数学模型 PnP求解方法 DLT直接线性变换法 EPnP EPnP的特点 步骤 理论推倒 1.控制点及齐次重心坐标系 2.控制点的选择 3.计算控制点在相机坐标系下的 ...

最新文章

  1. 验证插件——jquery.validate.js
  2. ios 图片自动轮播
  3. C/C++多线程编程之一】VC6.0安装pthread
  4. couchbase java 手册_couchbase的使用 java
  5. verilog中generate语句的使用
  6. Python实战技术 - Python虚拟隔离环境 和 Docker技术
  7. 编译调试Apache HTTP Server
  8. plsqldev解决中文乱码问题
  9. python天下无敌表情包_这套打遍天下无敌手的“算我输”表情包 从哪儿蹦出来的?...
  10. CorelDRAW如何设置填充颜色和边框颜色
  11. iOS 此应用需要开发者更新以在此ios版本上运行
  12. 2020python考试题库_大学mooc2020用Python玩转数据期末考试公众号答案
  13. 001 | “版绘童印”——疫情时代下版画在儿童插画中应用研究 | 大学生创新训练项目申请书 | 极致技术工厂
  14. 胆结石的发病原因有哪些?
  15. 2018年SCI论文--整合GEO数据挖掘完整复现 四 :差异表达(GSE65635)
  16. android sd卡名称,Android系统中SD卡各文件夹名称及功能详解
  17. 在Excel工作簿中显示网络图片
  18. 白话VPB(volume parameter block)
  19. ping++接入企业付款
  20. 收入时间序列——之模型探索篇

热门文章

  1. 计算机管理 灰色,详解电脑任务管理器变成灰色不可用的解决方法
  2. ylinux系统找到软件_你的 linux 上都有什么值得推荐的软件?
  3. Revealing Module(揭示模块)模式
  4. C语言的getc()和putc()函数
  5. matlab的foramt
  6. 经典前端框架,一个时代的落幕:如何看待layui 官网将于 2021年10月13日 进行下线?
  7. [论文笔记]Vision-based Control of 3D Facial Animation
  8. 手机话费充值和手机流量充值 API
  9. 使用d2rq把mysql转化为rdf_D2RQ
  10. C语言 十进制转换为二进制