本文想讨论rootBA代码中BA问题构造的过程。
float和double的实例化中,构造的流程是一致的。而这里,主要需要关注的就是load_normalized_bal_problem<datatype>这个模板类的构建
#ifdef ROOTBA_INSTANTIATIONS_FLOAT
// load dataset
auto bal_problem = load_normalized_bal_problem<float>(
options.dataset, &summary.dataset, &summary.timing);
// run solver
bundle_adjust_manual(bal_problem, options.solver, &summary.solver,
&summary.timing);
// postprocess
bal_problem.postprocress(options.dataset, &summary.timing);
#else
LOG(FATAL) << "Compiled without float support.";
它有很多种重载形式,我们需要关注的是如下的形式
template <class Scalar>
BalProblem<Scalar> load_normalized_bal_problem(
const BalDatasetOptions& options, DatasetSummary* dataset_summary,
PipelineTimingSummary* timing_summary)
通过配置文件中参数的选择,我们可以使用不同的求解器
BalProblem<double> bal_problem;
bal_problem.set_quiet(options.quiet);
switch (input_type) {
case BalDatasetOptions::DatasetType::ROOTBA:
bal_problem.load_rootba(options.input);
break;
case BalDatasetOptions::DatasetType::BAL:
bal_problem.load_bal(options.input);
break;
case BalDatasetOptions::DatasetType::BUNDLER:
bal_problem.load_bundler(options.input);
break;
我们关注一下lood_bal的加载方式,它是针对bal数据集进行适配后的接口
template <typename Scalar>
BalProblem<Scalar>::BalProblem(const std::string& path) {
load_bal(path);
}
template <typename Scalar>
void BalProblem<Scalar>::load_bal(const std::string& path) {
FILE* fptr = std::fopen(path.c_str(), "r");
if (fptr == nullptr) {
LOG(FATAL) << "Could not open '{}'"_format(path);
};
try {
// parse header
int num_cams;
int num_lms;
int num_obs;
fscan_or_throw(fptr, "%d", &num_cams);
fscan_or_throw(fptr, "%d", &num_lms);
fscan_or_throw(fptr, "%d", &num_obs);
CHECK_GT(num_cams, 0);
CHECK_GT(num_lms, 0);
CHECK_GT(num_obs, 0);
这里做的事情其实很简单,就是读取bal数据集,然后得到landmark的位置和camera的位姿相关信息,存储到BalProblem的类成员中 cameras_, landmarks_,值得注意的是,观测存储到了landmarks_中。并且,因为Bal使用了z轴向内,y轴向上的相机坐标系,代码里还一顿操作把两个轴都换成了反方向。
此外,作者load problem的两个参数dataset_summary 和 timing_summary是两个结构体,目的是为了汇报dataset的数据和时间的数据。
在加载完数据后,作者还添加了预处理操作。包括对于问题normalize,添加绕动,和过滤太近的特征点。
// normalize to fixed scale and center (as double, since there are some
// overflow issues with float for large problems)
if (options.normalize) {
bal_problem.normalize(options.normalization_scale);
}
// perturb state if sigmas are positive
bal_problem.perturb(options.rotation_sigma, options.translation_sigma,
options.point_sigma, options.random_seed);
// Filter observations of points closer than threshold to the camera
bal_problem.filter_obs(options.init_depth_threshold);
总结一下,问题加载的部分,作者主要目的是为了对于不同的数据集类型对于接口进行重载,适配。
这部分比较有意思的地方在于,作者把问题的构建和求解分离开来。另外,利用结构体传参记录数据信息,求解耗时等参数,也是非常细心的。