目标
本教程将学习如何使用重建API进行稀疏重建。
- 加载包含图像路径列表的文件。
- 运行libmv重建流程。
- 使用Viz显示获得的结果。
代码
#include <iostream>
#include <fstream>
static void help() {
cout
<< "\n------------------------------------------------------------------------------------\n"
<< " 此程序演示了OpenCV结构化从运动(SFM)模块中的多视图重建功能。\n"
<< " 它从一组2D图像重建场景 \n"
<< " 用法:\n"
<< " example_sfm_scene_reconstruction <path_to_file> <f> <cx> <cy>\n"
<< " 其中:path_to_file 是系统中包含\n"
<< " 用于重建的图像列表的文件的绝对路径。 \n"
<< " f 是以像素为单位的焦距。 \n"
<< " cx 是图像主点x坐标(像素)。 \n"
<< " cy 是图像主点y坐标(像素)。 \n"
<< "------------------------------------------------------------------------------------\n\n"
<< endl;
static int getdir(const string _filename, vector<String> &files)
}
ifstream myfile(_filename.c_str());
{
if (!myfile.is_open()) {
cout << "无法读取文件:" << _filename << endl;
exit(0);
} else {;
size_t found = _filename.find_last_of("/\\");
string line_str, path_to_file = _filename.substr(0, found);
while ( getline(myfile, line_str) )
files.push_back(path_to_file+string("/")+line_str);
return
}
int main(int argc, char* argv[]) 1;
}
// 读取输入参数
{
help();
{
// 解析图像路径
} else {;
}
getdir( argv[1], images_paths );
// 建立内参矩阵
cx = atof(argv[3]), cy = atof(argv[4]);
0, f, cy,
0, 0, 1);
0, 0, 1);
bool is_projective = true;
vector<Mat> Rs_est, ts_est, points3d_estimated;
reconstruct(images_paths, Rs_est, ts_est, K, points3d_estimated, is_projective);
cout << "\n----------------------------\n" << endl;
cout << "重建结果:" << endl;
cout << "============================" << endl;
cout << "估计的3D点:" << points3d_estimated.size() << endl;
cout << "估计的相机位姿:" << Rs_est.size() << endl;
cout << "细化后的内参矩阵:" << endl << K << endl << endl;
cout << "3D可视化:" << endl;
cout << "============================" << endl;
window.setWindowSize(
Size(500,500));
window.setWindowPosition(
Point(150,150));
window.setBackgroundColor();
cout << "恢复点云... ";
vector<Vec3f> point_cloud_est;
for (int i = 0; i < points3d_estimated.size(); ++i)
point_cloud_est.push_back(
Vec3f(points3d_estimated[i]));
cout << "[完成]" << endl;
cout << "恢复相机位姿... ";
vector<Affine3d> path;
for (size_t i = 0; i < Rs_est.size(); ++i)
path.push_back(
Affine3d(Rs_est[i],ts_est[i]));
cout << "[完成]" << endl;
if ( point_cloud_est.size() > 0 )
{
cout << "渲染点云... ";
viz::WCloud cloud_widget(point_cloud_est, viz::Color::green());
window.showWidget("point_cloud", cloud_widget);
cout << "[完成]" << endl;
}
else
{
cout << "无法渲染点云:点云为空" << endl;
}
if ( path.size() > 0 )
{
cout << "渲染相机位姿... ";
window.showWidget(
"cameras_frames_and_lines",
viz::WTrajectory(path, viz::WTrajectory::BOTH, 0.1, viz::Color::green()));
window.setViewerPose(path[0]);
cout << "[完成]" << endl;
}
else
{
cout << "无法渲染相机位姿:路径为空" << endl;
}
cout << endl << "按'q'关闭所有窗口... " << endl;
window.spin();
int main(int argc, char* argv[]) 0;
}
用于指定图像或矩形大小的模板类。
定义 types.hpp:335
Viz3d 类表示一个 3D 可视化窗口。此类是隐式共享的。
定义 viz3d.hpp:68
这个3D小部件表示一条轨迹。
定义 widgets.hpp:628
void reconstruct(InputArrayOfArrays points2d, OutputArray Ps, OutputArray points3d, InputOutputArray K, bool is_projective=false)
通过执行自标定,从2D对应关系重建3D点。
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3
解释
首先,我们需要加载包含图像路径列表的文件,以便为重建API提供数据。
/home/eriba/software/opencv_contrib/modules/sfm/samples/data/images/resized_IMG_2889.jpg
/home/eriba/software/opencv_contrib/modules/sfm/samples/data/images/resized_IMG_2890.jpg
/home/eriba/software/opencv_contrib/modules/sfm/samples/data/images/resized_IMG_2891.jpg
/home/eriba/software/opencv_contrib/modules/sfm/samples/data/images/resized_IMG_2892.jpg
...
int getdir(const string _filename, vector<string> &files)
{
if (!myfile.is_open()) {
cout << "无法读取文件:" << _filename << endl;
exit(0);
} else {;
} else {
string line_str;
files.push_back(path_to_file+string("/")+line_str);
files.push_back(line_str);
}
int main(int argc, char* argv[]) 1;
}
其次,构建的容器将用于为重建API提供数据。重要的是要说明,估计的结果必须存储在vector<Mat>中。在本例中,这是用于真实图像的重载签名,它从图像中内部提取并计算使用DAISY描述符的稀疏2D特征,以便使用FlannBasedMatcher进行匹配并构建轨迹结构。
bool is_projective = true;
vector<Mat> Rs_est, ts_est, points3d_estimated;
reconstruct(images_paths, Rs_est, ts_est, K, points3d_estimated, is_projective);
cout << "\n----------------------------\n" << endl;
cout << "重建结果:" << endl;
cout << "============================" << endl;
cout << "估计的3D点:" << points3d_estimated.size() << endl;
cout << "估计的相机位姿:" << Rs_est.size() << endl;
cout << "细化后的内参矩阵:" << endl << K << endl << endl;
最后,获得的结果将在Viz中显示。
使用和结果
为了运行此示例,我们需要指定图像路径文件的路径、摄像机的焦距以及中心投影坐标(以像素为单位)。
1. Middlebury 寺庙
使用以下图像序列[1]和以下相机参数,我们可以计算稀疏的3D重建。
./example_sfm_scene_reconstruction image_paths_file.txt 800 400 225
下图显示了获得的相机运动以及估计的稀疏3D重建。
2. 圣家堂
使用以下图像序列[2]和以下相机参数,我们可以计算稀疏的3D重建。
./example_sfm_scene_reconstruction image_paths_file.txt 350 240 360
下图显示了获得的相机运动以及估计的稀疏3D重建。
[1] http://vision.middlebury.edu/mview/data
[2] Penate Sanchez, A. 和 Moreno-Noguer, F. 和 Andrade Cetto, J. 和 Fleuret, F. (2014)。LETHA:从高质量输入中学习用于低质量图像中3D姿态估计。三维视觉国际会议论文集 (3DV)。 URL