0. 前言
本文是根据ceres官方教程内容ceres-solver官方教程链接,再结合自己理解的一个ceres快速学习笔记。
在博文ceres快速教材及学习笔记(一)hello,world!中,我们学习了

最小二乘问题数学模型;
弄清楚了各个参数的意义;
利用学习的最小二乘问题数学模型和ceres解决了一个最简单的最小二乘问题;

在博文ceres快速教材及学习笔记(二)曲线拟合,稍复杂的例子中,我们学会了怎样使用多个参数块,如何添加多个误差项。接下来在此基础上我们来求解更加复杂的问题,以处理如何使不同的误差项有不同代价函数以及参数块的情况。
在博文,ceres快速教材及学习笔记(三)鲍威尔方程Powell’s Function,中何使不同的误差项有不同代价函数以及参数块的情况。
接下来,在《视觉slam十四讲》第十讲ceres例子(源码在这里)中,我们将学会bundle adjustment。学会处理每个误差项输出维度不为一的情况

1.Bundle Adjustment
1.1准备工作

该项目分为以下几个步骤以实现bundle adjustment。

设置参数
读取ground truth 真实值
将真实值转化格式后输出initial.ply
基于真实值加入perturbation,也就是噪声,来模拟实际情况中获得的数据。
基于加了噪声的数据进行bundle adjustment。
输出BA之后的数据生成final.ply

在项目提供一个problem-16-22106-pre.txt文件:

16 22106 83718
0 0     -3.859900e+02 3.871200e+02
1 0     -3.844000e+01 4.921200e+02
2 0     -6.679200e+02 1.231100e+02
7 0     -5.991800e+02 4.079300e+02
12 0     -7.204300e+02 3.143400e+02
13 0     -1.151300e+02 5.548999e+01
0 1     3.838800e+02 -1.529999e+01

其含义,在提供该数据的官方网站上能找到:

<num_cameras> <num_points> <num_observations>
<camera_index_1> <point_index_1> <x_1> <y_1>
...
<camera_index_num_observations> <point_index_num_observations> <x_num_observations> <y_num_observations>
<camera_1>
...
<camera_num_cameras>
<point_1>
...
<point_num_points>

翻译
假设相机数量为m,路标点数量为n,观测数量为ob,则

<m> <n> <ob>
<观测1使用的相机序号> <观测1看到的路标点> <此次观测的路标点在图像中的位置x_1> <此次观测的路标点在图像中的位置y_1>
...
<观测ob使用的相机序号> <观测ob看到的路标点> <观测1看到的路标点x_ob> <观测1看到的路标点y_ob>
<相机1参数>
...
<相机m参数>
<路标点1参数>
...
<路标点n参数>

1.2问题思考

思考以下问题
首先,回答以下问题:(以下参数的各个含义点击这里):

1.3.代码讲解

先来看main函数

int main(int argc, char** argv)
{    //设置参数BundleParams params(argc,argv);  // set the parameters here.google::InitGoogleLogging(argv[0]);std::cout << params.input << std::endl;if(params.input.empty()){std::cout << "Usage: bundle_adjuster -input <path for dataset>";return 1;}//求解BASolveProblem(params.input.c_str(), params);return 0;
}

进入SolveProblem函数

void SolveProblem(const char* filename, const BundleParams& params)
{//读取文件BALProblem bal_problem(filename);// show some information here ...std::cout << "bal problem file loaded..." << std::endl;std::cout << "bal problem have " << bal_problem.num_cameras() << " cameras and "<< bal_problem.num_points() << " points. " << std::endl;std::cout << "Forming " << bal_problem.num_observations() << " observatoins. " << std::endl;// store the initial 3D cloud points and camera pose..//储存初始的(未BA前的)3d点云位置以及相机位置if(!params.initial_ply.empty()){bal_problem.WriteToPLYFile(params.initial_ply);}std::cout << "beginning problem..." << std::endl;// add some noise for the intial value//给camera,points都加上噪音srand(params.random_seed);bal_problem.Normalize();bal_problem.Perturb(params.rotation_sigma, params.translation_sigma,params.point_sigma);std::cout << "Normalization complete..." << std::endl;//开始构建最小二乘问题啦Problem problem;//具体构建的细节在这个函数里BuildProblem(&bal_problem, &problem, params);std::cout << "the problem is successfully build.." << std::endl;//配置求解器Solver::Options options;SetSolverOptionsFromFlags(&bal_problem, params, &options);options.gradient_tolerance = 1e-16;options.function_tolerance = 1e-16;Solver::Summary summary;Solve(options, &problem, &summary);std::cout << summary.FullReport() << "\n";//导出未BA后的3d点云位置以及相机位置// write the result into a .ply file.   if(!params.final_ply.empty()){bal_problem.WriteToPLYFile(params.final_ply);  // pay attention to this: ceres doesn't copy the value into optimizer, but implement on raw data! }
}

接下来看看BuildProblem(&bal_problem, &problem, params);看他如何构建BA问题

void BuildProblem(BALProblem* bal_problem, Problem* problem, const BundleParams& params)
{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();//构建points参数类型的优化变量double* cameras = bal_problem->mutable_cameras();//构建cameras参数类型的优化变量// Observations is 2 * num_observations long array observations// [u_1, u_2, ... u_n], where each u_i is two dimensional, the x // and y position of the observation. 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//在这里构建代价函数costfunction每个误差块是以一个points和一个cameras为输入的cost_function = SnavelyReprojectionError::Create(observations[2*i + 0], observations[2*i + 1]);// If enabled use Huber's loss function. //设置是否开启核函数LossFunction* loss_function = params.robustify ? new HuberLoss(1.0) : NULL;// Each observatoin corresponds to a pair of a camera and a point // which are identified by camera_index()[i] and point_index()[i]// respectively.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);}}

接下来看看SnavelyReprojectionErrorcostfunction

class SnavelyReprojectionError
{
public:SnavelyReprojectionError(double observation_x, double observation_y):observed_x(observation_x),observed_y(observation_y){}
//重载()以获得一个仿函数functor
template<typename T>bool operator()(const T* const camera,const T* const point,T* residuals)const{                  // camera[0,1,2] are the angle-axis rotationT predictions[2];CamProjectionWithDistortion(camera, point, predictions);//输出维度是2,代表着你在图像中观测到的路标点的像素坐标residuals[0] = predictions[0] - T(observed_x);residuals[1] = predictions[1] - T(observed_y);return true;}static ceres::CostFunction* Create(const double observed_x, const double observed_y){//在这里返回代价函数,这里是自动求导的,模版参数分别为<仿函数类型,误差项输出维度r,cameras维度s_1,points维度s_2>return (new ceres::AutoDiffCostFunction<SnavelyReprojectionError,2,9,3>(new SnavelyReprojectionError(observed_x,observed_y)));}private:double observed_x;double observed_y;
};

更多《计算机视觉与图形学》知识,可关注下方公众号:

ceres快速教材及学习笔记(四)bundl adjustment,《视觉slam十四讲》第十讲ceres例子相关推荐

  1. Python快速编程入门#学习笔记03# |第二章 :Python基础(代码格式、标识符关键字、变量和数据类型、数字类型以及运算符)

    全文目录 ==先导知识== 学习目标: 2.1 代码格式 2.1.1 注释 2.1.2 缩进 2.1.3 语句换行 2.2 标识符和关键字 2.2.1 标识符 2.2.2 关键字 2.3 变量和数据类 ...

  2. 刘海洋 · LaTeX 不快速的入门 学习笔记

    刘海洋 · LaTeX 不快速的入门 学习笔记 网址链接 : 刘海洋 · LaTeX 不快速的入门 - 跟着大神学习最纯正的 LaTeX 知识 一.组织文档结构 1. 文档基本结构 以document ...

  3. Python快速编程入门#学习笔记01# |第一章 :Python基础知识 (Python发展历程、常见的开发工具、import模块导入)

    全文目录 ==先导知识== 1 认识Python 1.1.1 Python的发展历程 1.1.2 Python语言的特点 2. Python解释器的安装与Python程序运行 1.2.1 安装Pyth ...

  4. Tensorflow2.0学习笔记(一)北大曹健老师教学视频1-4讲

    Tensorflow2.0学习笔记(一)北大曹健老师教学视频1-4讲 返回目录 这个笔记现在是主要根据北京大学曹健老师的视频写的,这个视频超级棒,非常推荐. 第一讲 常用函数的使用(包含了很多琐碎的函 ...

  5. 视觉SLAM笔记(3) 视觉SLAM框架

    视觉SLAM笔记(3) 视觉SLAM框架 1. 经典框架 2. 视觉里程计 3. 后端优化 4. 回环检测 5. 建图 5.1. 度量地图 5.2. 拓扑地图 1. 经典框架 相机在场景中运动的过程, ...

  6. 快速傅里叶变换 FFT 学习笔记

    文章目录 FFT ( 快速傅里叶变换 ) 学习笔记 参考文章: 多项式 系数表示法 点值表示法 复数 前置芝士 向量 弧度制 定义 运算法则 单位根 快速傅里叶变换: 快速傅里叶逆变换 (IFFT): ...

  7. 快速幂 算法学习笔记

    快速幂 学习笔记 文章目录 快速幂 学习笔记 概念与使用缘由 两种方法 1.用位运算的方法 位运算原理 位运算所需要用到的运算符 主程序如下: 程序解析 概念与使用缘由 快速幂,顾名思义,就是用比一个 ...

  8. Go语学习笔记 - gorm使用 - gorm处理错误 Web框架Gin(十)

    学习笔记,写到哪是哪. 接着上一篇文章:Go语学习笔记 - gorm使用 - 原生sql.命名参数.Rows.ToSQL | Web框架Gin(九)_的博客-CSDN博客 目前gorm对数据库的一些操 ...

  9. 《TensorFlow 2.0深度学习算法实战教材》学习笔记(四、TensorFlow 进阶)

    合并与分割 合并 张量的合并可以使用拼接(Concatenate)和堆叠(Stack)操作实现,拼接并不会产生新的维度,而堆叠会创建新维度.选择使用拼接还是堆叠操作来合并张量,取决于具体的场景是否需要 ...

最新文章

  1. Matlab R2018a 64位安装教程
  2. 【NLP】bert4vec:一个基于预训练的句向量生成工具
  3. Myeclipse 2014配置SVN详细图解
  4. 自动文本摘要(automatic text summarization)目前的研究方法分类
  5. jdk8 Function
  6. android inflate,Android 关于inflate
  7. ecshop flow.php goods_number,修复ECSHOP一重要BUG,当商品设置数量优惠时,加入不同属性的商品数量优惠判断错误...
  8. 桌面的计算机被删掉了怎么调出来,误删了电脑桌面图标怎么办——一波超简单的操作,分分钟搞定它...
  9. Springboot+idea的一个bug(Unregistering JMX-exposed beans on shutdown)
  10. oracle默认的优化器,Oracle优化器相关参数设置
  11. arcgis 经纬度转大地坐标_MapGIS实现大地坐标到经纬度(地理坐标)的换算
  12. 储存profiles是什么意思_save profile是什么意思
  13. TOMCAT HTPPS
  14. BSP板机支持包、linux启动分析、ARM裸机编程
  15. vue中实现文字超过2行... 展开-收起(兼容ie)
  16. 小火狐进化_《乐贝星空》宠物大全 解析小火狐三阶进化
  17. 台达PLC模拟量或台达伺服电机控制模块程序案例
  18. 微信根据Media_id下载录音报错readfile(): Peer certificate CN=`mp.weixin.qq.com‘ did not match expected CN=`file
  19. 解决数据迁移过程中主键冲突的问题
  20. JSON与对象、list数组的相互转化

热门文章

  1. 探地雷达(GPR)检测地下钢筋,并可视化钢筋
  2. IC layout 培训班招收学员啦
  3. 面对行业分析家和敏捷专家都认可的API测试,我们为什么会望而却步?
  4. 免费的Revit模型的查看软件
  5. 数据库实现电商管理系统
  6. ANSYS Maxwell 2D螺线管磁场分析
  7. 试用期工资不得低于劳动合同的百分之多少
  8. 关于华为认证方向的选择?
  9. arcpy中拆分获取FeatureClass中各类型地物要素到单独的shp中,类似于splitShp的功能(地理国情监测)
  10. Facebook 是怎么保证缓存一致性的