注意:本文中的代码必须使用OpenCV3.0或以上版本进行编译,因为很多函数是3.0以后才加入的。

目录:

  • SfM介绍
  • 小孔相机模型
  • 坐标系
  • 内参矩阵
  • 外参矩阵
  • 相机的标定

SfM介绍

SfM的全称为Structure from Motion,即通过相机的移动来确定目标的空间和几何关系,是三维重建的一种常见方法。它与Kinect这种3D摄像头最大的不同在于,它只需要普通的RGB摄像头即可,因此成本更低廉,且受环境约束较小,在室内和室外均能使用。但是,SfM背后需要复杂的理论和算法做支持,在精度和速度上都还有待提高,所以目前成熟的商业应用并不多。 
本系列介绍SfM中的基本原理与算法,借助OpenCV实现一个简易的SfM系统。

小孔相机模型

在计算机视觉中,最常用的相机模型就是小孔模型(小孔成像模型),它将相机的透镜组简化为一个小孔,光线透过小孔在小孔后方的像面上成像,如下图所示。 
 
由上图可知,小孔模型成的是倒像,为了表述与研究的方便,我们常常将像面至于小孔之前,且到小孔的距离仍然是焦距f,这样的模型与原来的小孔模型是等价的,只不过成的是正像,符合人的直观感受。在这种情况下,往往将小孔称作光心(Optical Center)。 
 
小孔模型是一种理想相机模型,没有考虑实际相机中存在的场曲、畸变等问题。在实际使用时,这些问题可以通过在标定的过程中引入畸变参数解决,所以小孔模型仍然是目前最广泛使用的相机模型。

坐标系

为了用数学研究SfM,我们需要坐标系。在SfM中主要有两类坐标系,一类为相机坐标系,一类为世界坐标系。在本系列中,所以坐标系均为右手坐标系。 
相机坐标系以相机的光心(小孔)作为原点,X轴为水平方向,Y轴为竖直方向,Z轴指向相机所观察的方向。 
世界坐标系的原点可以任意选择,与相机的具体位置无关。 

内参矩阵

设空间中有一点P,若世界坐标系与相机坐标系重合,则该点在空间中的坐标为(X, Y, Z),其中Z为该点到相机光心的垂直距离。设该点在像面上的像为点p,像素坐标为(x, y),那么(X, Y, Z)和(x, y)有什么关系呢? 
 
由上图可知,这是一个简单的相似三角形关系,从而得到

x=fXZ,   y=fYZ

但是,图像的像素坐标系原点在左上角,而上面公式假定原点在图像中心,为了处理这一偏移,设光心在图像上对应的像素坐标为 (cx,cy) ,则

x=fXZ+cx,   y=fYZ+cy

将以上关系表示为矩阵形式,有

Zxy1=f000f0cxcy1XYZ

其中,将矩阵

K=f000f0cxcy1

称为内参矩阵,因为它只和相机自身的内部参数有关(焦距,光心位置)。

外参矩阵

一般情况下,世界坐标系和相机坐标系不重合,这时,世界坐标系中的某一点P要投影到像面上时,先要将该点的坐标转换到相机坐标系下。设P在世界坐标系中的坐标为X,P到光心的垂直距离为s(即上文中的Z),在像面上的坐标为x,世界坐标系与相机坐标系之间的相对旋转为矩阵R(R是一个三行三列的旋转矩阵),相对位移为向量T(三行一列),则

sx=K[RX+T]

其中 RX+T  即为P在相机坐标系下的坐标,使用齐次坐标改写上式,有

sx=K[RT][X1]

其中 [RT] 是一个三行四列的矩阵,称为外参矩阵,它和相机的参数无关,只与相机在世界坐标系中的位置有关。 

相机的标定

相机的标定,即为通过某个已知的目标,求取相机内参矩阵的过程。最常用的标定目标就是棋盘格。用相机对棋盘格从不同角度拍摄多张照片,然后将这些照片导入标定程序或算法,即可自动求出相机的内参。

相机标定的方法和工具,我在这篇文章中已有详细的介绍,这里就不再细述了。在此提醒一下,之后的文章中若无特殊说明,所有相机均假定内参已知。

目录:

  • 极线约束与本征矩阵
  • 特征点提取与匹配
  • 三维重建
  • 测试

极线约束与本征矩阵

在三维重建前,我们先研究一下同一点在两个相机中的像的关系。假设在世界坐标系中有一点p,坐标为X,它在1相机中的像为x1,在2相机中的像为x2(注意x1和x2为齐次坐标,最后一个元素是1),如下图。 
 
设X到两个相机像面的垂直距离分别为s1和s2,且这两个相机具有相同的内参矩阵K,与世界坐标系之间的变换关系分别为[R1  T1]和[R2  T2],那么我们可以得到下面两个等式

s1x1=K(R1X+T1)s2x2=K(R2X+T2)

由于K是可逆矩阵,两式坐乘K的逆,有

1

设 K1x1=x′1 , K1x2=x′2 ,则有

s1x′1=R1X+T1s2x′2=R2X+T2

我们一般称 x′1 和 x′2 为归一化后的像坐标,它们和图像的大小没有关系,且原点位于图像中心。 
由于世界坐标系可以任意选择,我们将世界坐标系选为第一个相机的相机坐标系,这时 R1=I, T1=0 。上式则变为

s1x′1=Xs2x′2=R2X+T2

将第一式带入第二式,有

s2x′2=s1R2x′1+T2

x′2 和 T2 都是三维向量,它们做外积(叉积)之后得到另外一个三维向量 T2x′2 (其中 为外积的矩阵形式, T2x′2 代表 T2×x′2 ),且该向量垂直于 x′2 和 T2 ,再用该向量对等式两边做内积,有

0=s1(T2x′2)TR2x′1

x′2T2R2x′1=0

令 E=T2R2  有

x′2Ex′1=0

可以看出,上式是同一点在两个相机中的像所满足的关系,它和点的空间坐标、点到相机的距离均没有关系,我们称之为极线约束,而矩阵 E 则称为关于这两个相机的本征矩阵。如果我们知道两幅图像中的多个对应点(至少5对),则可以通过上式解出矩阵 E ,又由于 E 是由 T2 和 R2 构成的,可以从E中分解出 T2 和 R2 。 
如何从 E 中分解出两个相机的相对变换关系(即 T2 和 R2 ),背后的数学原理比较复杂,好在OpenCV为我们提供了这样的方法,在此就不谈原理了。

特征点提取与匹配

从上面的分析可知,要求取两个相机的相对关系,需要两幅图像中的对应点,这就变成的特征点的提取和匹配问题。对于图像差别较大的情况,推荐使用SIFT特征,因为SIFT对旋转、尺度、透视都有较好的鲁棒性。如果差别不大,可以考虑其他更快速的特征,比如SURF、ORB等。 
本文中使用SIFT特征,由于OpenCV3.0将SIFT包含在了扩展部分中,所以官网上下载的版本是没有SIFT的,为此需要到这里下载扩展包,并按照里面的说明重新编译OpenCV(哎~真麻烦,-_-!)。如果你使用其他特征,就不必为此辛劳了。 
下面的代码负责提取图像特征,并进行匹配。

void extract_features(vector<string>& image_names,vector<vector<KeyPoint>>& key_points_for_all,vector<Mat>& descriptor_for_all,vector<vector<Vec3b>>& colors_for_all)
{key_points_for_all.clear();descriptor_for_all.clear();Mat image;//读取图像,获取图像特征点,并保存Ptr<Feature2D> sift = xfeatures2d::SIFT::create(0, 3, 0.04, 10);for (auto it = image_names.begin(); it != image_names.end(); ++it){image = imread(*it);if (image.empty()) continue;vector<KeyPoint> key_points;Mat descriptor;//偶尔出现内存分配失败的错误sift->detectAndCompute(image, noArray(), key_points, descriptor);//特征点过少,则排除该图像if (key_points.size() <= 10) continue;key_points_for_all.push_back(key_points);descriptor_for_all.push_back(descriptor);vector<Vec3b> colors(key_points.size());for (int i = 0; i < key_points.size(); ++i){Point2f& p = key_points[i].pt;colors[i] = image.at<Vec3b>(p.y, p.x);}colors_for_all.push_back(colors);}
}void match_features(Mat& query, Mat& train, vector<DMatch>& matches)
{vector<vector<DMatch>> knn_matches;BFMatcher matcher(NORM_L2);matcher.knnMatch(query, train, knn_matches, 2);//获取满足Ratio Test的最小匹配的距离float min_dist = FLT_MAX;for (int r = 0; r < knn_matches.size(); ++r){//Ratio Testif (knn_matches[r][0].distance > 0.6*knn_matches[r][1].distance)continue;float dist = knn_matches[r][0].distance;if (dist < min_dist) min_dist = dist;}matches.clear();for (size_t r = 0; r < knn_matches.size(); ++r){//排除不满足Ratio Test的点和匹配距离过大的点if (knn_matches[r][0].distance > 0.6*knn_matches[r][1].distance ||knn_matches[r][0].distance > 5 * max(min_dist, 10.0f))continue;//保存匹配点matches.push_back(knn_matches[r][0]);}
}

需要重点说明的是,匹配结果往往有很多误匹配,为了排除这些错误,这里使用了Ratio Test方法,即使用KNN算法寻找与该特征最匹配的2个特征,若第一个特征的匹配距离与第二个特征的匹配距离之比小于某一阈值,就接受该匹配,否则视为误匹配。当然,也可以使用Cross Test(交叉验证)方法来排除错误。

得到匹配点后,就可以使用OpenCV3.0中新加入的函数findEssentialMat()来求取本征矩阵了。得到本征矩阵后,再使用另一个函数对本征矩阵进行分解,并返回两相机之间的相对变换R和T。注意这里的T是在第二个相机的坐标系下表示的,也就是说,其方向从第二个相机指向第一个相机(即世界坐标系所在的相机),且它的长度等于1。

bool find_transform(Mat& K, vector<Point2f>& p1, vector<Point2f>& p2, Mat& R, Mat& T, Mat& mask)
{//根据内参矩阵获取相机的焦距和光心坐标(主点坐标)double focal_length = 0.5*(K.at<double>(0) + K.at<double>(4));Point2d principle_point(K.at<double>(2), K.at<double>(5));//根据匹配点求取本征矩阵,使用RANSAC,进一步排除失配点Mat E = findEssentialMat(p1, p2, focal_length, principle_point, RANSAC, 0.999, 1.0, mask);if (E.empty()) return false;double feasible_count = countNonZero(mask);cout << (int)feasible_count << " -in- " << p1.size() << endl;//对于RANSAC而言,outlier数量大于50%时,结果是不可靠的if (feasible_count <= 15 || (feasible_count / p1.size()) < 0.6)return false;//分解本征矩阵,获取相对变换int pass_count = recoverPose(E, p1, p2, R, T, focal_length, principle_point, mask);//同时位于两个相机前方的点的数量要足够大if (((double)pass_count) / feasible_count < 0.7)return false;return true;
}

三维重建

现在已经知道了两个相机之间的变换矩阵,还有每一对匹配点的坐标。三维重建就是通过这些已知信息还原匹配点在空间当中的坐标。在前面的推导中,我们有

s2x2=K(R2X+T2)

这个等式中有两个未知量,分别是 s2 和 X 。用 x2 对等式两边做外积,可以消去 s2 ,得

0=x2K(R2X+T2)

整理一下可以得到一个关于空间坐标X的线性方程

x2KR2X=x2T2

解上述方程,即可求取X。其几何意义相当于分别从两个相机的光心作过 x1 和 x2 的延长线,延长线的焦点即为方程的解,如文章最上方的图所示。由于这种方法和三角测距类似,因此这种重建方式也被称为三角化(triangulate)。OpenCV提供了该方法,可以直接使用。

void reconstruct(Mat& K, Mat& R, Mat& T, vector<Point2f>& p1, vector<Point2f>& p2, Mat& structure)
{//两个相机的投影矩阵[R T],triangulatePoints只支持float型Mat proj1(3, 4, CV_32FC1);Mat proj2(3, 4, CV_32FC1);proj1(Range(0, 3), Range(0, 3)) = Mat::eye(3, 3, CV_32FC1);proj1.col(3) = Mat::zeros(3, 1, CV_32FC1);R.convertTo(proj2(Range(0, 3), Range(0, 3)), CV_32FC1);T.convertTo(proj2.col(3), CV_32FC1);Mat fK;K.convertTo(fK, CV_32FC1);proj1 = fK*proj1;proj2 = fK*proj2;//三角化重建triangulatePoints(proj1, proj2, p1, p2, structure);
}

测试

我用了下面两幅图像进行测试 

得到了着色后的稀疏点云,是否能看出一点轮廓呢?!

 

图片中的两个彩色坐标系分别代表两个相机的位置。 
在接下来的文章中,会将相机的个数推广到任意多个,成为一个真正的SfM系统。

关于源代码的使用 
代码是用VS2013写的,OpenCV版本为3.0且包含扩展部分,如果不使用SIFT特征,可以修改源代码,然后使用官方未包含扩展部分的库。软件运行后会将三维重建的结果写入Viewer目录下的structure.yml文件中,在Viewer目录下有一个SfMViewer程序,直接运行即可读取yml文件并显示三维结构。

目录:

  • 问题简化
  • 求第三个相机的变换矩阵
  • 加入更多图像
  • 代码实现
  • 测试
  • 思考
  • 下载

问题简化

终于有时间来填坑了,这次一口气将双目重建扩展为多目重建吧。首先,为了简化问题,我们要做一个重要假设:用于多目重建的图像是有序的,即相邻图像的拍摄位置也是相邻的。多目重建本身比较复杂,我会尽量说得清晰,如有表述不清的地方,还请见谅并欢迎提问。


求第三个相机的变换矩阵

由前面的文章我们知道,两个相机之间的变换矩阵可以通过findEssentialMat以及recoverPose函数来实现,设第一个相机的坐标系为世界坐标系,现在加入第三幅图像(相机),如何确定第三个相机(后面称为相机三)到到世界坐标系的变换矩阵呢?

最简单的想法,就是沿用双目重建的方法,即在第三幅图像和第一幅图像之间提取特征点,然后调用findEssentialMat和recoverPose。那么加入第四幅、第五幅,乃至更多呢?随着图像数量的增加,新加入的图像与第一幅图像的差异可能越来越大,特征点的提取变得异常困难,这时就不能再沿用双目重建的方法了。

那么能不能用新加入的图像和相邻图像进行特征匹配呢?比如第三幅与第二幅匹配,第四幅与第三幅匹配,以此类推。当然可以,但是这时就不能继续使用findEssentialMat和recoverPose来求取相机的变换矩阵了,因为这两个函数求取的是相对变换,比如相机三到相机二的变换,而我们需要的是相机三到相机一的变换。有人说,既然知道相机二到相机一的变换,又知道相机到三到相机二的变换,不就能求出相机三到相机一的变换吗?实际上,通过这种方式,你只能求出相机三到相机一的旋转变换(旋转矩阵R),而他们之间的位移向量T,是无法求出的。这是因为上面两个函数求出的位移向量,都是单位向量,丢失了相机之间位移的比例关系。

说了这么多,我们要怎么解决这些问题?现在请出本文的主角——solvePnP和solvePnPRansac。根据opencv的官方解释,该函数根据空间中的点与图像中的点的对应关系,求解相机在空间中的位置。也就是说,我知道一些空间当中点的坐标,还知道这些点在图像中的像素坐标,那么solvePnP就可以告诉我相机在空间当中的坐标。solvePnP和solvePnPRansac所实现的功能相同,只不过后者使用了随机一致性采样,使其对噪声更鲁棒,本文使用后者。

好了,有这么好的函数,怎么用于我们的三维重建呢?首先,使用双目重建的方法,对头两幅图像进行重建,这样就得到了一些空间中的点,加入第三幅图像后,使其与第二幅图像进行特征匹配,这些匹配点中,肯定有一部分也是图像二与图像一之间的匹配点,也就是说,这些匹配点中有一部分的空间坐标是已知的,同时又知道这些点在第三幅图像中的像素坐标,嗯,solvePnP所需的信息都有了,自然第三个相机的空间位置就求出来了。由于空间点的坐标都是世界坐标系下的(即第一个相机的坐标系),所以由solvePnP求出的相机位置也是世界坐标系下的,即相机三到相机一的变换矩阵。


加入更多图像

通过上面的方法得到相机三的变换矩阵后,就可以使用上一篇文章提到的triangulatePoints方法将图像三和图像二之间的匹配点三角化,得到其空间坐标。为了使之后的图像仍能使用以上方法求解变换矩阵,我们还需要将新得到的空间点和之前的三维点云融合。已经存在的空间点,就没必要再添加了,只添加在图像二和三之间匹配,但在图像一和图像三中没有匹配的点。如此反复。 
 
为了方便点云的融合以及今后的扩展,我们需要存储图像中每个特征点在空间中的对应点。在代码中我使用了一个二维列表,名字为correspond_struct_idx,correspond_struct_idx[i][j]代表第i幅图像第j个特征点所对应的空间点在点云中的索引,若索引小于零,说明该特征点在空间当中没有对应点。通过此结构,由特征匹配中的queryIdx和trainIdx就可以查询某个特征点在空间中的位置。


代码实现

前一篇文章的很多代码不用修改,还可以继续使用,但是程序的流程有了较大变化。首先是初始化点云,也就是通过双目重建方法对图像序列的头两幅图像进行重建,并初始化correspond_struct_idx。

void init_structure(Mat K,vector<vector<KeyPoint>>& key_points_for_all, vector<vector<Vec3b>>& colors_for_all,vector<vector<DMatch>>& matches_for_all,vector<Point3f>& structure,vector<vector<int>>& correspond_struct_idx,vector<Vec3b>& colors,vector<Mat>& rotations,vector<Mat>& motions)
{//计算头两幅图像之间的变换矩阵vector<Point2f> p1, p2;vector<Vec3b> c2;Mat R, T;   //旋转矩阵和平移向量Mat mask;   //mask中大于零的点代表匹配点,等于零代表失配点get_matched_points(key_points_for_all[0], key_points_for_all[1], matches_for_all[0], p1, p2);get_matched_colors(colors_for_all[0], colors_for_all[1], matches_for_all[0], colors, c2);find_transform(K, p1, p2, R, T, mask);//对头两幅图像进行三维重建maskout_points(p1, mask);maskout_points(p2, mask);maskout_colors(colors, mask);Mat R0 = Mat::eye(3, 3, CV_64FC1);Mat T0 = Mat::zeros(3, 1, CV_64FC1);reconstruct(K, R0, T0, R, T, p1, p2, structure);//保存变换矩阵rotations = { R0, R };motions = { T0, T };//将correspond_struct_idx的大小初始化为与key_points_for_all完全一致correspond_struct_idx.clear();correspond_struct_idx.resize(key_points_for_all.size());for (int i = 0; i < key_points_for_all.size(); ++i){correspond_struct_idx[i].resize(key_points_for_all[i].size(), -1);}//填写头两幅图像的结构索引int idx = 0;vector<DMatch>& matches = matches_for_all[0];for (int i = 0; i < matches.size(); ++i){if (mask.at<uchar>(i) == 0)continue;correspond_struct_idx[0][matches[i].queryIdx] = idx;correspond_struct_idx[1][matches[i].trainIdx] = idx;++idx;}
}

初始点云得到后,就可以使用增量方式重建剩余图像,注意,在代码中为了方便实现,所有图像之间的特征匹配已经事先完成了,并保存在matches_for_all这个列表中。增量重建的关键是调用solvePnPRansac,而这个函数需要空间点坐标和对应的像素坐标作为参数,有了correspond_struct_idx,实现这个对应关系的查找还是很方便的,如下。

void get_objpoints_and_imgpoints(vector<DMatch>& matches,vector<int>& struct_indices, vector<Point3f>& structure, vector<KeyPoint>& key_points,vector<Point3f>& object_points,vector<Point2f>& image_points)
{object_points.clear();image_points.clear();for (int i = 0; i < matches.size(); ++i){int query_idx = matches[i].queryIdx;int train_idx = matches[i].trainIdx;int struct_idx = struct_indices[query_idx];if (struct_idx < 0) continue;object_points.push_back(structure[struct_idx]);image_points.push_back(key_points[train_idx].pt);}
}

之后调用solvePnPRansac得到相机的旋转向量和位移,由于我们使用的都是旋转矩阵,所以这里要调用opencv的Rodrigues函数将旋转向量变换为旋转矩阵。之后,使用上一篇文章中用到的reconstruct函数对匹配点进行重建(三角化),不过为了适用于多目重建,做了一些简单修改。

void reconstruct(Mat& K, Mat& R1, Mat& T1, Mat& R2, Mat& T2, vector<Point2f>& p1, vector<Point2f>& p2, vector<Point3f>& structure)
{//两个相机的投影矩阵[R T],triangulatePoints只支持float型Mat proj1(3, 4, CV_32FC1);Mat proj2(3, 4, CV_32FC1);R1.convertTo(proj1(Range(0, 3), Range(0, 3)), CV_32FC1);T1.convertTo(proj1.col(3), CV_32FC1);R2.convertTo(proj2(Range(0, 3), Range(0, 3)), CV_32FC1);T2.convertTo(proj2.col(3), CV_32FC1);Mat fK;K.convertTo(fK, CV_32FC1);proj1 = fK*proj1;proj2 = fK*proj2;//三角重建Mat s;triangulatePoints(proj1, proj2, p1, p2, s);structure.clear();structure.reserve(s.cols);for (int i = 0; i < s.cols; ++i){Mat_<float> col = s.col(i);col /= col(3);  //齐次坐标,需要除以最后一个元素才是真正的坐标值structure.push_back(Point3f(col(0), col(1), col(2)));}
}

最后,将重建结构与之前的点云进行融合。

void fusion_structure(vector<DMatch>& matches, vector<int>& struct_indices, vector<int>& next_struct_indices,vector<Point3f>& structure, vector<Point3f>& next_structure,vector<Vec3b>& colors,vector<Vec3b>& next_colors)
{for (int i = 0; i < matches.size(); ++i){int query_idx = matches[i].queryIdx;int train_idx = matches[i].trainIdx;int struct_idx = struct_indices[query_idx];if (struct_idx >= 0) //若该点在空间中已经存在,则这对匹配点对应的空间点应该是同一个,索引要相同{next_struct_indices[train_idx] = struct_idx;continue;}//若该点在空间中已经存在,将该点加入到结构中,且这对匹配点的空间点索引都为新加入的点的索引structure.push_back(next_structure[i]);colors.push_back(next_colors[i]);struct_indices[query_idx] = next_struct_indices[train_idx] = structure.size() - 1;}
}

整个增量方式重建图像的代码大致如下。

//初始化结构(三维点云)
init_structure(K,key_points_for_all,colors_for_all,matches_for_all,structure,correspond_struct_idx,colors,rotations,motions);//增量方式重建剩余的图像
for (int i = 1; i < matches_for_all.size(); ++i)
{vector<Point3f> object_points;vector<Point2f> image_points;Mat r, R, T;//Mat mask;//获取第i幅图像中匹配点对应的三维点,以及在第i+1幅图像中对应的像素点get_objpoints_and_imgpoints(matches_for_all[i], correspond_struct_idx[i], structure,key_points_for_all[i+1], object_points,image_points);//求解变换矩阵solvePnPRansac(object_points, image_points, K, noArray(), r, T);//将旋转向量转换为旋转矩阵Rodrigues(r, R);//保存变换矩阵rotations.push_back(R);motions.push_back(T);vector<Point2f> p1, p2;vector<Vec3b> c1, c2;get_matched_points(key_points_for_all[i], key_points_for_all[i + 1], matches_for_all[i], p1, p2);get_matched_colors(colors_for_all[i], colors_for_all[i + 1], matches_for_all[i], c1, c2);//根据之前求得的R,T进行三维重建vector<Point3f> next_structure;reconstruct(K, rotations[i], motions[i], R, T, p1, p2, next_structure);//将新的重建结果与之前的融合fusion_structure(matches_for_all[i], correspond_struct_idx[i], correspond_struct_idx[i + 1],structure, next_structure,colors,c1);
}

测试

我用了八幅图像进行测试,正如问题简化中所要求的那样,图像是有序的。 
 
程序的大部分时间花在特征提取和匹配上,真正的重建过程耗时很少。最终结果如下。 
 
图中每个彩色坐标系都代表一个相机。


思考

  • 这个多目三维重建程序,要求图像必须是有序的,如果图像无序,比如只是对某个目标在不同角度的随意拍摄,程序应该如何修改?
  • 增量式三维重建方法,有一个很大的缺点——随着图像的不断增加,误差会不断累积,最后误差过大以至于完全偏离重建的目标,怎么解决?

有兴趣的读者可以思考一下上面两个问题,第二个问题比较难,我会在下一篇文章中详细介绍。


下载

程序使用VS2015开发,OpenCV版本为3.1且包含扩展部分,如果不使用SIFT特征,可以修改源代码,然后使用官方未包含扩展部分的库。软件运行后会将三维重建的结果写入Viewer目录下的structure.yml文件中,在Viewer目录下有一个SfMViewer程序,直接运行即可读取yml文件并显示三维结构。

三维重建-opencv实现sfm相关推荐

  1. OpenCV实现SfM(四):Bundle Adjustment

    http://blog.csdn.net/AIchipmunk/article/details/52433884 OpenCV实现SfM(四):Bundle Adjustment 标签: opencv ...

  2. 【算法】多视图几何三维重建+增量式SfM

    多视图几何三维重建的基本原理: 从两个或者多个视点观察同一景物,已获得在多个不同的视角下对景物的多张感知图像,运用三角测量的基本原理计算图像像素间位置偏差,获得景物的三维深度信息,这一个过程与人类观察 ...

  3. Opencv+contrib SfM 配置及测试

    Opencv+contrib SfM 配置及测试 文章目录 Opencv+contrib SfM 配置及测试 Opencv+contrib dependencies opencv: SfM: buil ...

  4. OpenCV实现SfM(一):相机模型

    注意:本文中的代码必须使用OpenCV3.0或以上版本进行编译,因为很多函数是3.0以后才加入的. 目录: 文章目录 #SfM介绍 SfM的全称为Structure from Motion,即通过相机 ...

  5. 双目立体视觉源代码 双目立体视觉匹配程序 双目视觉3d成像(三维重构图像处理) 基于双目视觉的深度计算和三维重建 opencv写的双目视觉摄像机标定和三维重建代码

    双目视觉/双目标定源码/图片集标定匹配三维重建坐标计算OpenCV 1.双目立体视觉源代码(包括标定,匹配,三维重建) 2.双目视觉实验图片集(双目立体视觉中使用的标准实验图,适合初学者进 行实验使用 ...

  6. OpenCV的三维重建实现方案

    OpenCV的三维重建实现方案 摘 要 1 引言 2 三维重建的基本原理 3 基于OpenCV的三维重建 4 实验结果 5 结束与展望 参考文献 摘 要 本文以计算机视觉三维重建技术为研究对象,分析了 ...

  7. 一种基于OpenCV的三维重建实现方案

    一种基于OpenCV的三维重建实现方案 来源:淘金者论文范文 作者:Www.TaoJz.Com 日期:08/30/09 摘 要 本文以计算机视觉三维重建技术为研究对象,分析了开放计算机视觉函数库Ope ...

  8. 计算机视觉——SFM与三位重建

    第五章:SFM与三维重建 1. 三维重建简述 2. 对极几何 2.1 对极几何简述 2.2 五点共面约束 2.3 几个相关概念 2.4 对应点的约束 3. 两种矩阵 3.1 基础矩阵F(对极几何的代数 ...

  9. SfM多视图三维点云重建--【VS2015+OpenCV3.4+PCL1.8】

    难点 在完成两视图三维重建之后,接下来就是进行多视图重建.多视图重建的难点在于如何确定第 i i i( i i i>2)个相机到世界坐标系的位姿变换矩阵. 两视图重建时,是将第一个相机所在的坐标 ...

最新文章

  1. 微信小程序web-view使用
  2. 如何计算服务限流的配额
  3. OpenCV | OpenCV哈里斯 (Harris)角点检测
  4. 新书《深入理解Kafka:核心设计与实践原理》上架,感谢支持~
  5. lynda ux_如何进入UX领域
  6. 云图说 | 华为云医疗智能体,智联大健康,AI药物研发
  7. 【Cinemachine智能相机教程】VirtualCamera(二):Body属性
  8. Sentinel服务熔断无配置_客户自定义限流处理_削峰填谷_流量控制_速率控制_服务熔断_服务降级---微服务升级_SpringCloud Alibaba工作笔记0049
  9. 深圳大学二本计算机软件,深圳大学是几本(深圳大学是一本还是二本)
  10. 解決win7打开EXE文件总是出现安全警告
  11. 利用Flash XMLSocket实现”服务器推”技术
  12. Java开发笔记(一百三十)Swing的选择框
  13. Atitit ppt转换文本txt 目录 1.1. Atitit word ppt excel convert txt bp 等文档转换纯文本问题最佳实践.docx 1 1.2. 跨语言转换Open
  14. 动作捕捉协助中国电力科学研究院建立边云协同电力自主巡检系统
  15. zfs文件服务器上传失败,解决 ZFS 文件系统问题
  16. IDEA快捷生成方法
  17. oracle 中的.dbf,郑怡:请问各位大师:oracle中dbf文件是什么文件?
  18. Can#39;t locate Tk.pm
  19. 基于SSH框架开发的毕业生求职报名系统
  20. python蜂鸣器天空之城频率_如何把两个程序合在一起实现(小车超声波避障+蜂鸣器音乐《天空之城》)...

热门文章

  1. java基础思维导图
  2. Eclipse中各种文件【默认编码格式设置】,防止乱码等问题
  3. 苹果6s解除耳机模式_出大问题!苹果或将砍掉3D Touch?
  4. 杭电1430康托 bfs(java)
  5. 北大OJ百练——4074:积水量(C语言)
  6. 详解为什么32位系统只能用4G内存.
  7. java注释日志打印_java 日志文件打印
  8. php unset 多可以什么_PHP unset() 详解
  9. 思科路由器动态NAT配置
  10. 开发函数计算的正确姿势——运行 Selenium Java