ceres快速教材及学习笔记(四)bundl adjustment,《视觉slam十四讲》第十讲ceres例子
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);}}
接下来看看SnavelyReprojectionError
costfunction
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例子相关推荐
- Python快速编程入门#学习笔记03# |第二章 :Python基础(代码格式、标识符关键字、变量和数据类型、数字类型以及运算符)
全文目录 ==先导知识== 学习目标: 2.1 代码格式 2.1.1 注释 2.1.2 缩进 2.1.3 语句换行 2.2 标识符和关键字 2.2.1 标识符 2.2.2 关键字 2.3 变量和数据类 ...
- 刘海洋 · LaTeX 不快速的入门 学习笔记
刘海洋 · LaTeX 不快速的入门 学习笔记 网址链接 : 刘海洋 · LaTeX 不快速的入门 - 跟着大神学习最纯正的 LaTeX 知识 一.组织文档结构 1. 文档基本结构 以document ...
- Python快速编程入门#学习笔记01# |第一章 :Python基础知识 (Python发展历程、常见的开发工具、import模块导入)
全文目录 ==先导知识== 1 认识Python 1.1.1 Python的发展历程 1.1.2 Python语言的特点 2. Python解释器的安装与Python程序运行 1.2.1 安装Pyth ...
- Tensorflow2.0学习笔记(一)北大曹健老师教学视频1-4讲
Tensorflow2.0学习笔记(一)北大曹健老师教学视频1-4讲 返回目录 这个笔记现在是主要根据北京大学曹健老师的视频写的,这个视频超级棒,非常推荐. 第一讲 常用函数的使用(包含了很多琐碎的函 ...
- 视觉SLAM笔记(3) 视觉SLAM框架
视觉SLAM笔记(3) 视觉SLAM框架 1. 经典框架 2. 视觉里程计 3. 后端优化 4. 回环检测 5. 建图 5.1. 度量地图 5.2. 拓扑地图 1. 经典框架 相机在场景中运动的过程, ...
- 快速傅里叶变换 FFT 学习笔记
文章目录 FFT ( 快速傅里叶变换 ) 学习笔记 参考文章: 多项式 系数表示法 点值表示法 复数 前置芝士 向量 弧度制 定义 运算法则 单位根 快速傅里叶变换: 快速傅里叶逆变换 (IFFT): ...
- 快速幂 算法学习笔记
快速幂 学习笔记 文章目录 快速幂 学习笔记 概念与使用缘由 两种方法 1.用位运算的方法 位运算原理 位运算所需要用到的运算符 主程序如下: 程序解析 概念与使用缘由 快速幂,顾名思义,就是用比一个 ...
- Go语学习笔记 - gorm使用 - gorm处理错误 Web框架Gin(十)
学习笔记,写到哪是哪. 接着上一篇文章:Go语学习笔记 - gorm使用 - 原生sql.命名参数.Rows.ToSQL | Web框架Gin(九)_的博客-CSDN博客 目前gorm对数据库的一些操 ...
- 《TensorFlow 2.0深度学习算法实战教材》学习笔记(四、TensorFlow 进阶)
合并与分割 合并 张量的合并可以使用拼接(Concatenate)和堆叠(Stack)操作实现,拼接并不会产生新的维度,而堆叠会创建新维度.选择使用拼接还是堆叠操作来合并张量,取决于具体的场景是否需要 ...
最新文章
- Matlab R2018a 64位安装教程
- 【NLP】bert4vec:一个基于预训练的句向量生成工具
- Myeclipse 2014配置SVN详细图解
- 自动文本摘要(automatic text summarization)目前的研究方法分类
- jdk8 Function
- android inflate,Android 关于inflate
- ecshop flow.php goods_number,修复ECSHOP一重要BUG,当商品设置数量优惠时,加入不同属性的商品数量优惠判断错误...
- 桌面的计算机被删掉了怎么调出来,误删了电脑桌面图标怎么办——一波超简单的操作,分分钟搞定它...
- Springboot+idea的一个bug(Unregistering JMX-exposed beans on shutdown)
- oracle默认的优化器,Oracle优化器相关参数设置
- arcgis 经纬度转大地坐标_MapGIS实现大地坐标到经纬度(地理坐标)的换算
- 储存profiles是什么意思_save profile是什么意思
- TOMCAT HTPPS
- BSP板机支持包、linux启动分析、ARM裸机编程
- vue中实现文字超过2行... 展开-收起(兼容ie)
- 小火狐进化_《乐贝星空》宠物大全 解析小火狐三阶进化
- 台达PLC模拟量或台达伺服电机控制模块程序案例
- 微信根据Media_id下载录音报错readfile(): Peer certificate CN=`mp.weixin.qq.com‘ did not match expected CN=`file
- 解决数据迁移过程中主键冲突的问题
- JSON与对象、list数组的相互转化