OpenMVG-GlobalSfM入门开发

SfM平台

过去一两年尝试自己搭个SfM的小平台,主要采用Global SfM的流程,所以在OpenMVG和Theia上二次开发花了不少时间。这篇文章先写下基于GlobalSfM流程下OpenMVG二次开发的主要接口,希望能对大家有些帮助。SfM的各类平台可以参考如下:

NameDownloadTypeOpen Source
Bundlerhttp://www.cs.cornell.edu/~snavely/bundler/Incremental SfMYes
Visual SFMhttp://www.ccwu.me/vsfm/Incremental SfMYes
1DSfMhttps://github.com/wilsonkl/SfM_InitGlobal SfMYes
DISCOhttp://vision.soic.indiana.edu/projects/disco/Global SfMYes
OpenMVGhttps://github.com/openMVG/openMVGGlobal & Incremental SfMYes
Theiahttps://github.com/sweeneychris/TheiaSfMGlobal & Incremental SfMYes
OpenSfMhttps://github.com/mapillary/OpenSfMIncremental SfMYes
SAMANTHAhttps://www.3dflow.net/technology/3df-samantha/Hierarchical SfMNo

OpenMVG配置

  1. github 地址https://github.com/openMVG/openMVG
  2. doc 地址https://openmvg.readthedocs.io/en/latest/
  3. 说明:Linux下配置很方便,可以参考官方配置说明。win下配置可以参考这篇文章

OpenMVG使用与评估

  1. 评估数据集: 在Global SfM相关论文中经常比较Strecha这个数据集,从论文上给的地址已经404,所幸在OpenMVG的repos中还是可以找到:https://github.com/openMVG/SfM_quality_evaluation
  2. 使用: 下载数据集之后,可以按照数据集中Readme的提示进行测试,但是会出现错误:

(1): 安装OpenMVG
(2):python EvaluationLauncher.py [OpenMVG构建的可执行文件夹对应的绝对路径] [数据集路径] [输出路径]

例如:

python EvaluationLauncher.py /home/user/openMVG_Build/Linux-x86_64-RELEASE ./Benchmarking_Camera_Calibration_2008 ./Benchmarking_Camera_Calibration_2008_out
  1. 错误说明:下载数据集之后,按照数据集中Readme的提示进行测试, 应该会出现无法评估的错误。这是因为评估脚本EvaluationLauncher.py 源码中的问题。可以对EvaluationLauncher.py 文件中修改下:
gt_camera_dir = os.path.join(input_eval_dir, directory, "gt_dense_cameras")

修改为
gt_camera_dir = os.path.join(input_eval_dir, directory)
  1. 评估结果:以castle-P19为例,位姿的精度评估最终结果如下,我这里把参数稍微调整了下,所以评估精度会比默认参数更高一点。
    在这里插入图片描述

OpenMVG 二次开发之 BAL格式文件接口

  1. 起因: 康奈尔大学课题组和Google-Ceres采用的都是BAL格式的观测文件,而OpenMVG本身不支持BAL格式读入或者写出. 因此,在某些需求下,需要进行拓展BAL接口.
  2. BAL文件格式:可以参考这个网站: http://grail.cs.washington.edu/projects/bal/
  3. 源码分析 ( 以openMVG_main_GlobalSfM为例):
    1.1 读写流程分析:

在这里插入图片描述在这里插入图片描述

Note: openMVG Global SfM的pipeline可以参考博文https://blog.csdn.net/weixin_41109672/article/details/107908290, 这篇文章的博主提到angle_axis是什么,这个是3x1的旋转向量, 模值表示旋转角度, 方向向量表示旋转轴方向

1.2 这里提供Writing 部分的WriteBALfile函数, 仅供参考. Loading类似, 只需要按照Flowchart, 填充Features_Provider & Matches_Provider & SfM_Data的结构体变量即可. 这里,我把WriteBALfile作为成员函数添加进GlobalSfMReconstructionEngine_RelativeMotions类.

void GlobalSfMReconstructionEngine_RelativeMotions::WriteBALfile(
        const char* fname){

    {
        IndexT num_obs(0);
        for ( int i = 0; i < sfm_data_.structure.size(); ++i) {
            auto tmp_observations = sfm_data_.structure[i].obs;
            for ( int j =0; j< tmp_observations.size(); ++j) {
                num_obs++;
            }
        }

        //PO added code segment: export observations into bal file format
        {
            std::fstream of(fname,std::ios::out);

            // store header info
            int bal_num_views, bal_num_pts, bal_num_obs;
            bal_num_views =sfm_data_.poses.size();
            bal_num_pts = sfm_data_.structure.size();
            bal_num_obs = num_obs;
            of<<bal_num_views<<" "<<bal_num_pts<<" "<<bal_num_obs<<std::endl;

            // store track info
            for ( int i = 0; i < sfm_data_.structure.size(); ++i) {
                auto tmp_observations = sfm_data_.structure[i].obs;
                for ( auto it = tmp_observations.begin(); it != tmp_observations.end(); ++it){
                    const size_t imaIndex = it->first;
                    auto & pt = it->second.x;
                    Eigen::Vector3d normalized_coord;
                    normalized_coord<<pt.x(),pt.y(),1;

//                    std::shared_ptr<cameras::Pinhole_Intrinsic> tmp_intrinsic;
                    auto tmp_intrinsic = (this->sfm_data_.intrinsics[0]);
                    IntrinsicBase *tmp_intrinsic1=tmp_intrinsic.get();
                    cameras::Pinhole_Intrinsic *tmp_pinhole_intrinsic=(cameras::Pinhole_Intrinsic*)tmp_intrinsic1;
                    auto Kinv = tmp_pinhole_intrinsic->Kinv();
                    normalized_coord = -Kinv*normalized_coord;
                    of<<imaIndex<<" "<<i<<" "<<normalized_coord(0)<<" "<<normalized_coord(1)<<std::endl;
                }
            }


            // store pose info
            SfM_Data& tmp_data =this->sfm_data_;
            Poses& tmp_poses = tmp_data.poses;

            for (int i = 0; i < bal_num_views; ++i) {
                geometry::Pose3& tmp_pose=tmp_data.poses[i];
                double ang_axis[3];
                ceres::RotationMatrixToAngleAxis(tmp_pose.rotation().data(),ang_axis);
                of<<ang_axis[0]<<std::endl;
                of<<ang_axis[1]<<std::endl;
                of<<ang_axis[2]<<std::endl;

                of<<tmp_pose.translation()[0]<<std::endl;
                of<<tmp_pose.translation()[1]<<std::endl;
                of<<tmp_pose.translation()[2]<<std::endl;

                of<<1<<std::endl;
                of<<0<<std::endl;
                of<<0<<std::endl;
            }

            // store 3D info
            auto tmp_structure = tmp_data.structure;
            for ( int i = 0; i < bal_num_pts; ++i) {
                Landmark& landmark = tmp_structure[i];
                of<<landmark.X(0)<<std::endl;
                of<<landmark.X(1)<<std::endl;
                of<<landmark.X(2)<<std::endl;
            }

            of.close();
        }

        //PO end *************************************************
    }
}

1.3 SfM_Data结构体: OpenMVG的核心结构体. 以下是我对其做的注释.

struct SfM_Data
{
/// Considered views
  Views views;
  // using Views = Hash_Map<IndexT, std::shared_ptr<View>>;
  //其中View结构体的成员变量:
  //(1) 图像的路径:  std::string s_Img_path;
  //(2) 图像的id:  IndexT id_view;`在这里插入代码片`
  //(3) 内参数和位姿的id:  IndexT id_intrinsic, id_pose;
  //(4) 图像的大小:  IndexT ui_width, ui_height;
  
  /// Considered poses (indexed by view.id_pose)
  Poses poses;
  //using Poses = Hash_Map<IndexT, geometry::Pose3>; 
  //其中Pose3的成员变量:
  //实际上Pose3就是相机3x3旋转矩阵变量和一个3x1的相机位置变量
  
  /// Considered camera intrinsics (indexed by view.id_intrinsic)
  Intrinsics intrinsics;
  //using Intrinsics = Hash_Map<IndexT, std::shared_ptr<cameras::IntrinsicBase>>;
  //IntrinsicBase的成员变量:
  //实际上IntrinsicBase结构中只有width和height变量
  
  /// Structure (3D points with their 2D observations)
  Landmarks structure;
  //using Landmarks = Hash_Map<IndexT, Landmark>;
  //其中Landmark的成员变量:
  //(1)3d points: Vec3 X;
  //(2)3D点对应的观测图像特征点:Observations obs;//using Observations = Hash_Map<IndexT, Observation>;
  //其中IndexT为ImageID, Observation的成员变量:
  //(1)2d image points:  Vec2 x;
  //(2)特征点的id:  IndexT id_feat;

/// Controls points (stored as Landmarks (id_feat has no meaning here))
  Landmarks control_points;
  
  /// Root Views path
  std::string s_root_path;
};

暂时先写这么多,有什么需求可以提, 然后我再更新对应部分, 后续有空会更新theia vision的bal接口, distort_bal_flie等部分, ceres开发的坑以及GlobalSfM 到目前的发展情况.

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SIPANGYIYOU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值