openmvg is a lightweight SfM open source library that can be run step by step. It implements both incremental and global algorithms.
Document address: https://openmvg.readthedocs.io/en/latest/
github homepage address: https://github.com/openMVG/openMVG
1 Compile and install
The installation of openmvg is relatively simple, the first is to install dependencies:
$ sudo apt‐get install libxxf86vm1 libxxf86vm‐dev libpng‐dev libjpeg‐dev libtiff‐dev libxi‐dev libxrandr‐dev graphviz
Then clone the openmvg source code:
$ git clone ‐‐recursive https://github.com/openMVG/openMVG.git
Finally, use cmake to compile and install:
$ mkdir openMVG_Build && cd openMVG_Build
$ cmake ‐DCMAKE_BUILD_TYPE=RELEASE ../openMVG/src/
$ cmake ‐‐build . ‐‐target install
There may be errors caused by too low eigen version during compilation:
/usr/include/eigen3/Eigen/SparseCholesky:34:2: error: #error The SparseCholesky module has nothing to offer in MPL2 only mode
#error The SparseCholesky module has nothing to offer in MPL2 only mode
^~~~~
In file included from /usr/include/eigen3/Eigen/SparseCholesky:37:0,
from /home/czq/softwares/openMVG/src/openMVG/multiview/rotation_averaging_l1.cpp:37:
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:266:72: error: ‘AMDOrdering’ does not name a type; did you mean ‘COLAMDOrdering’?
plate<typename _MatrixType, int _UpLo = Lower, typename _Ordering = AMDOrdering<typename _MatrixType::StorageIndex> > class SimplicialLLT;
^~~~~~~~~~~
COLAMDOrdering
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:266:83: error: expected ‘>’ before ‘<’ token
ame _MatrixType, int _UpLo = Lower, typename _Ordering = AMDOrdering<typename _MatrixType::StorageIndex> > class SimplicialLLT;
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:267:72: error: ‘AMDOrdering’ does not name a type; did you mean ‘COLAMDOrdering’?
plate<typename _MatrixType, int _UpLo = Lower, typename _Ordering = AMDOrdering<typename _MatrixType::StorageIndex> > class SimplicialLDLT;
^~~~~~~~~~~
COLAMDOrdering
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:267:83: error: expected ‘>’ before ‘<’ token
ame _MatrixType, int _UpLo = Lower, typename _Ordering = AMDOrdering<typename _MatrixType::StorageIndex> > class SimplicialLDLT;
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:268:72: error: ‘AMDOrdering’ does not name a type; did you mean ‘COLAMDOrdering’?
plate<typename _MatrixType, int _UpLo = Lower, typename _Ordering = AMDOrdering<typename _MatrixType::StorageIndex> > class SimplicialCholesky;
^~~~~~~~~~~
COLAMDOrdering
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:268:83: error: expected ‘>’ before ‘<’ token
ame _MatrixType, int _UpLo = Lower, typename _Ordering = AMDOrdering<typename _MatrixType::StorageIndex> > class SimplicialCholesky;
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:515:60: error: template argument 3 is invalid
typedef internal::traits<SimplicialLDLT<MatrixType,UpLo> > LDLTTraits;
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:515:62: error: template argument 1 is invalid
typedef internal::traits<SimplicialLDLT<MatrixType,UpLo> > LDLTTraits;
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:516:59: error: template argument 3 is invalid
typedef internal::traits<SimplicialLLT<MatrixType,UpLo> > LLTTraits;
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:516:62: error: template argument 1 is invalid
typedef internal::traits<SimplicialLLT<MatrixType,UpLo> > LLTTraits;
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h: In member function ‘void Eigen::SimplicialCholesky<_MatrixType, _UpLo, _Ordering>::_solve_impl(const Eigen::MatrixBase<OtherDerived>&, Eigen::MatrixBase<OtherDerived>&) const’:
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:605:27: error: qualified-id in declaration before ‘(’ token
LDLTTraits::getL(Base::m_matrix).solveInPlace(dest);
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:607:26: error: qualified-id in declaration before ‘(’ token
LLTTraits::getL(Base::m_matrix).solveInPlace(dest);
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:616:27: error: qualified-id in declaration before ‘(’ token
LDLTTraits::getU(Base::m_matrix).solveInPlace(dest);
^
/usr/include/eigen3/Eigen/src/SparseCholesky/SimplicialCholesky.h:618:26: error: qualified-id in declaration before ‘(’ token
LLTTraits::getU(Base::m_matrix).solveInPlace(dest);
^
/home/czq/softwares/openMVG/src/openMVG/multiview/rotation_averaging_l1.cpp: In function ‘bool openMVG::rotation_averaging::l1::internal::SolveIRLS(const RelativeRotations&, openMVG::rotation_averaging::l1::Matrix3x3Arr&, const sMat&, unsigned int, double)’:
/home/czq/softwares/openMVG/src/openMVG/multiview/rotation_averaging_l1.cpp:460:53: error: template argument 3 is invalid
using Linear_Solver_T = Eigen::SimplicialLDLT<sMat>;
^
/home/czq/softwares/openMVG/src/openMVG/multiview/rotation_averaging_l1.cpp:462:3: error: ‘Linear_Solver_T’ was not declared in this scope
Linear_Solver_T linear_solver;
^~~~~~~~~~~~~~~
/home/czq/softwares/openMVG/src/openMVG/multiview/rotation_averaging_l1.cpp:463:3: error: ‘linear_solver’ was not declared in this scope
linear_solver.analyzePattern(A.transpose() * A);
^~~~~~~~~~~~~
Generally, you only need to upgrade its version to 3.4.0. The command to check the eigen version is:
pkg-config --modversion eigen3
For the tutorial on reinstalling eigen, please refer to: ubuntu reinstalling/upgrading eigen tutorial
2 run command
The official instructions of openmvg talk about how to run SfM, including using a script to run a line of commands (only when the image exif has internal reference information), and how to run it step by step, you can refer to here . But what needs to be explained here is that the code of openmvg has been modified later, but the usage documentation has not been completely updated. Therefore, if you just run according to the documentation, or many other tutorials (maybe just copying official documents or older ones), you may encounter errors (many examples cannot be run). After encountering some unknown error reports and stepping on a lot of pitfalls, I recorded the commands that can be run. Here, it is best to only change the root path of the output folder, which can be placed in any path you want, but it is best not to modify the names of some outputs, as it is easy to make mistakes. Another issue that needs attention is that it needs to be clear whether the image has exif information that can be read into the camera’s internal parameters, such as focal length. If not, then the camera’s internal parameters need to be input in the first step, which will be introduced separately below.
2.1 Image exif with camera internal reference information
# 首先给一些路径进行定义,方便下面描述,使用时使用自己的实际目录进行替换即可
# DIR_DATA: 图像数据存放路径
# DIR_OUTPUT: 存放输出结果的路径
# DIR_OM: 存放openmvg源码的路径
1. 初始化图像列表
openMVG_main_SfMInit_ImageListing -d DIR_OM/openMVG/src/openMVG/exif/sensor_width_database/sensor_width_camera_database.txt -i DIR_DATA -o DIR_OUTPUT
2. 计算特征
openMVG_main_ComputeFeatures -i DIR_OUTPUT/sfm_data.json -o DIR_OUTPUT
3. 生成图像对
openMVG_main_PairGenerator -i DIR_OUTPUT/sfm_data.json -o DIR_OUTPUT/imgpairs.bin
4. 图像匹配
openMVG_main_ComputeMatches -i DIR_OUTPUT/sfm_data.json -p DIR_OUTPUT/imgpairs.bin -o DIR_OUTPUT/matches.bin
5. 错误匹配点对滤除
openMVG_main_GeometricFilter -i DIR_OUTPUT/sfm_data.json -m DIR_OUTPUT/matches.bin -g f -o DIR_OUTPUT/matches.f.bin
6.1. 全局式SfM
openMVG_main_SfM -s GLOBAL -i DIR_OUTPUT/sfm_data.json -m DIR_OUTPUT/ -o DIR_OUTPUT/out_Global_Reconstruction
6.2. 增量式SfM
openMVG_main_SfM -s INCREMENTAL -i DIR_OUTPUT/sfm_data.json -m DIR_OUTPUT/ -o DIR_OUTPUT/out_Incremental_Reconstruction
7.1. 生成颜色(全局式)
openMVG_main_ComputeSfM_DataColor -i DIR_OUTPUT/out_Global_Reconstruction/sfm_data.bin -o DIR_OUTPUT/out_Global_Reconstruction/sfm_data_colorized.ply
7.2. 生成颜色(增量式)
openMVG_main_ComputeSfM_DataColor -i DIR_OUTPUT/out_Incremental_Reconstruction/sfm_data.bin -o DIR_OUTPUT/out_Incremental_Reconstruction/sfm_data_colorized.ply
The following is the experimental data and the final generated results:
Among them, the folder out_Global_Reconstruction
/ out_Incremental_Reconstruction
under which the final output results are stored should have the following content:
The ply file is point cloud data, which can be viewed and edited visually with cloudcompare or meshlab. The above result picture is the sfm_data_colorized.ply file opened with cloudcompare.
2.2 Parameter specification input internal reference information
To enter the internal reference information, you only need to modify it in the first step, and the other steps are consistent with 2.1. The way to manually enter the internal parameter k is to add -k
parameters:
1. 初始化图像列表
openMVG_main_SfMInit_ImageListing -d DIR_OM/openMVG/src/openMVG/exif/sensor_width_database/sensor_width_camera_database.txt -i DIR_DATA -o DIR_OUTPUT -k "6432;0;3000;0;6455;2000;0;0;1"
The -k
parameters are nine values delimited by double quotes and separated by semicolons, that is, the internal reference matrix K, arranged in row-major order, as follows:
"fx;0;cx;0;fy;cy;0;0;1"
The following is the experimental data and the final generated results:
3 Automatically run scripts
Running commands line by line is a bit too cumbersome, especially when there are many scenarios to run. So I wrote a script that automates the entire process. If you need to automate multiple processes, just add a for loop. For the script writing tutorial, please refer to the shell/bash script command tutorial .
#!/bin/bash
##################################################### variables ########################################################
# model name
model_name=ortho1_3_112
# sensor db path, change it according to your environment
db_path=../../../workspace/openmvg/openMVG/src/openMVG/exif/sensor_width_database/sensor_width_camera_database.txt
# input images folder
in_img_dir=../imgs/100-130/${model_name}
# output folder
out_dir=./${model_name}
# intrinsic matrix
k="3746.77;0;1988;0;3761.05;1326;0;0;1"
# json path
json_path=${out_dir}/sfm_data.json
# image pairs path
img_pair_path=${out_dir}/imgpairs.bin
# matches path
matches_path=${out_dir}/matches.bin
# refine matches path
refined_path=${out_dir}/matches.f.bin
# output incremental reconstruction folder
out_incremental_dir=${out_dir}/out_Incremental_Reconstruction
# incremental reconstruction model path
incremental_bin_path=${out_incremental_dir}/sfm_data.bin
# color ply path
out_incremental_color_path=${out_incremental_dir}/sfm_data_colorized.ply
# output global reconstruction folder
out_global_dir=${out_dir}/out_Global_Reconstruction
# global reconstruction model path
global_bin_path=${out_global_dir}/sfm_data.bin
# color ply path
out_global_color_path=${out_global_dir}/sfm_data_colorized.ply
##################################################### commands #########################################################
# initial image list
openMVG_main_SfMInit_ImageListing \
-d ${db_path} \
-i ${in_img_dir} \
-o ${out_dir} \
-k ${k}
wait
# compute features
openMVG_main_ComputeFeatures \
-i ${json_path} \
-o ${out_dir}
wait
# generate image pairs
openMVG_main_PairGenerator \
-i ${json_path} \
-o ${img_pair_path}
wait
# feature matching
openMVG_main_ComputeMatches \
-i ${json_path} \
-p ${img_pair_path} \
-o ${matches_path}
wait
# geometric filter
openMVG_main_GeometricFilter \
-i ${json_path} \
-m ${matches_path} \
-g f \
-o ${refined_path}
wait
# incremental reconstruction
openMVG_main_SfM \
-s INCREMENTAL \
-i ${json_path} \
-m ${out_dir} \
-o ${out_incremental_dir}
wait
# compute color for incremental model
openMVG_main_ComputeSfM_DataColor \
-i ${incremental_bin_path} \
-o ${out_incremental_color_path}
wait
# global reconstruction
openMVG_main_SfM \
-s GLOBAL \
-i ${json_path} \
-m ${out_dir} \
-o ${out_global_dir}
wait
# compute color for incremental model
openMVG_main_ComputeSfM_DataColor \
-i ${global_bin_path} \
-o ${out_global_color_path}