探测来至运动的结构在这一章,我们将讨论来至运动的结构的概念(SfM),或者从一个运动的相机拍摄到的图像中更好的推测提取出来的几何结构,我们使用OpenCV的Api函数来帮我们完成。让我们将我们使用的冗长的方法约束为使用单个摄像机,通常称为单目方法,并且是一组分离的和稀疏的视频帧而不是连续的视频流。这两个约束在很大程度上简化了系统,这个系统我们将在接下来的页码中进行描述,并且帮助我们理解任何SfM方法的原理。为了实现我们的方法,我们将跟随Hartley和Zisserman的脚步(后面称作H和Z),伴随着他们有创意的书——《计算机视觉中的多视觉几何》的第9章到第12章的记录。在这一章,我们将涉及到以下内容:1、来至运动结构的概念2、从一组图像中估计摄像机的运动3、重构场景4、从多个视图中重构5、可视化3D点云自始自终本章假定使用一个标定过的摄像机——一个先前标定过的摄像机。标定是在计算机视觉中普遍存在的操作,在OpenCV中得到很好的支持,我们可以使用在前面一张讨论的命令行方法来完成标定。因此,我们假定摄像机内参数存在,并且具体化到K矩阵中,K矩阵为摄像机标定过程的一个结果输出。为了在语言方面表达的清晰,从现在开始我们将单个摄像机称为场景的单个视图而不是光学和获取图像的硬件。一个摄像机在空间中有一个位置,一个观察的方向。在两个摄像机中,有一个平移成分(空间运动)和一个观察方向的旋转。我们同样对场景中的点统一一下术语,世界(world),现实(real),或者3D,意思是一样的,即我们真实世界存在的一个点。这同样适用于图像中的点或者2D,这些点在图像坐标系中,是在这个位置和时间上一些现实的3D点投影到摄像机传感器上形成的。在这一章的代码部分,你将注意参考《计算视觉中的多视觉几何》(MultipleViewGeometryinComputerVision),例如,//HZ9.12,这指的是这本书的第9章第12个等式。同样的,文本仅包括代码的摘录,而完整的代码包含在伴随着这本书的材料中。来至运动结构的概念第一个我们应当区别的是立体(Stereoorindeedanymultiview),使用标准平台的三维重建和SfM之间的差异。在两个或多个摄像机的平台中,假定我们已经知道了两个摄像机之间的运动,而在SfM中,我们实际上不知道这个运动并且我们希望找到它。标准平台,来至观察的一个过分简单的点,可以得到一个更加精确的3D几何的重构,因为在估计多个摄像机间距离和旋转时没有误差(距离和旋转已知)。实现一个SfM系统的第一步是找到相机之间的运动。OpenCV可以帮助我们在许多方式上获得这个运动,特别地,使用findFundamentalMat函数。让我们想一下选择一个SfM算法背后的目的。在很多情况下,我们希望获得场景的几何。例如,目标相对于相机的位置和他们的形状是什么。假定我们知道了捕获同一场景的摄像机之间的运动,从观察的一个合理的相似点,现在我们想重构这个几何。在计算视觉术语中称为三角测量(triangulation),并且有很多方法可以做这件事。它可以通过射线相交的方法完成,这里我们构造两个射线:来至于每个摄像机投影中心的点和每一个图像平面上的点。理想上,这两个射线在空间中将相交于真实世界的一个3D点(这个3D点在每个摄像机中成像),如下面图表展示:实际上,射线相交非常不可靠。H和Z推荐不使用它。这是因为两个射线通常不能相交,让我们回到使用连接两个射线的最短线段上的中间点。相反,H和Z建议一些方法来三角化3D点(trianglulate3Dpoints),这些方法中我们将在重构场景部分讨论两个。OpenCV现在的版本没有包含三角测量的API,因此,这部分我们将自己编写代码。学习完如何从两个视图恢复3D几何之后,我们将看到我们是怎么样加入更多的同一个场景的视图来获得更丰富的重构。在那时,大部分的SfM方法试图依靠束调整(BundleAdjustment)来优化我们的摄像机和3D点一束估计位置(thebundleofestimatedpositon),这部分内容在重构提纯部分(Refinementofthereconstructionsection)。OpenCV在新的图像拼接的工具箱内包含了用来束调整的方法。然而,使OpenCV和C++完美结合的工作是丰富的外部工具,这些工具可以很容易地整合到一起。因此,我们将看到如何如何整合一个外部的束调节器——灵巧的SSBA库。既然我们已经描述了使用OpenCV实现我们的SfM方法的一个概括,我们将看到每个分成是如何实现的。使用一对图像估计摄像机的运动事实上,在我们开始着手两个摄像机之间的运动之前,让我们检查一下我们手边用来执行这个操作的输入和工具。首先,我们有来至(希望并不是非常)不同空间位置的同一场景的两个图像。这是一个强大的资产,并且我们将确保使用它。现在工具都有了,我们应当看一下数学对象,来对我们的图像,相机和场景施加约束。两个非常有用的数学对象是基础矩阵(用F表示)和本征矩阵(用E表示)。除了本征矩阵是假设使用的标定的相机,它们非常相似。这种情况针对我们,所以我们将选着它。OpenCV函数仅允许我们通过findFundamentalMat函数找到基础矩阵。然而,我们非常简单地使用标定矩阵(calibrationmatrix)K从本征矩阵中获得基础矩阵,如下:Mat_doubleE=K.t()*F*K;//accordingtoHZ(9.12)本征矩阵,是一个3×3大小的矩阵,使用x’Ex=0在图像中的一点和另外一个图像中的一点之间施加了一个约束,这里x是图像一中的的一点,x’是图像二中与之相对应的一点。这非常有用,因为我们将要看到。我们使用的另一个重要的事实是本征矩阵是我们用来为我们的图像恢复两个相机的所有需要,尽管只有尺度,但是我们以后会得到。因此,如果我们获得了本征矩阵,我们知道每一个相机在空间中的位置,并且知道它们的观察方向,如果我们有足够这样的约束等式,那么我们可以简单地计算出这个矩阵。简单的因为每一个等式可以用来解决矩阵的一小部分。事实上,OpenCV允许我们仅使用7个点对来计算它,但是我们希望获得更多的点对来得到一个鲁棒性的解。使用丰富的特征描述子进行点匹配现在我们将使用我们的约束等式来计算本征矩阵。为了获得我们的约束,记住对于图像A中的每一个点我们必须在图像B中找到一个与之相对应的点。我们怎样完成这个匹配呢?简单地通过使用OpenCV的广泛的特征匹配框架,这个框架在过去的几年了已经非常成熟。在计算机视觉中,特征提取和描述子匹配是一个基础的过程,并且用在许多方法中来执行各种各样的操作。例如,检测图像中一个目标的位置和方向,或者通过给出一个查询图像在大数据图像中找到相似的图像。从本质上讲,提取意味着在图像中选择点,使得获得好的特征,并且为它们计算一个描述子。一个描述子是含有多个数据的向量,用来描述在一个图像中围绕着特征点的周围环境。不同的方法有不同的长度和数据类型来表示描述子矢量。匹配是使用它的描述子从另外一个图像中找到一组与之对应的特征。OpenCV提供了非常简单和有效的方法支持特征提取和匹配。关于特征匹配的更多信息可以在Chapter3少量(无)标记增强现实中找到。让我们检查一个非常简单特征提取和匹配方案://detectingkeypointsSurfFeatureDetectordetector();vectorKeyPointkeypoints1,keypoints2;detector.detect(img1,keypoints1);detector.detect(img2,keypoints2);//computingdescriptorsSurfDescriptorExtractorextractor;Matdescriptors1,descriptors2;extractor.compute(img1,keypoints1,descriptors1);extractor.compute(img2,keypoints2,descriptors2);//matchingdescriptorsBruteForceMatcherL2floatmatcher;vectorDMatchmatches;matcher.match(descriptors1,descriptors2,matches);你可能已经看到类似的OpenCV代码,但是让我们快速的回顾一下它。我们的目的是获得三个元素:两个图像的特征点,特征点的描述子,和两个特征集的匹配。OpenCV提供了一组特征检测器、描述子提取器和匹配器。在这个简单例子中,我们使用SurfFeatureDetector函数来获得SURF(Speeded-Up-RobustFeatures)特征的2D位置,并且使用SurfDescriptorExtractor函数来获得SURF描述子。我们使用一个brute-force匹配器来获得匹配,这是一个最直接方式来匹配两个特征集,该方法是通过比较第一个集中的每一个特征和第一个集的每一个特征并且获得最好的匹配来实现的。在下一个图像中,我们将看到两个图像上的特征点的匹配:(这两个图像来至于theFountain-P11sequence,可以在网址中找到:~strecha/multiview/denseMVS.html.)实际上,像我们执行的原始匹配(rawmatching),只有到达某一程度时执行效果才比较好并且许多匹配可能是错误的。因此,大多数SfM方法对原始匹配进行一些滤波方式来确保正确和减少错误。一种滤波方式叫做交叉检验滤波,它内置于OpenCV的brute-force匹配器中。也就是说,如果第一个图像的一个特征匹配第二个图像的一个特征,并且通过反向检查,第二个图像的特征也和第一个图像的特征匹配,那么这个匹配被认为是正确的。另一个常见的滤波机制(该机制用在提供的代码中)是基于同一场景的两个图像并且在他们之间存在某一种立体视觉关系,这样的一个事实基础之上来滤波的。在实践中,这个滤波器尝试采用鲁棒性的算法来计算这个基础矩阵,我们将会在寻找相机矩阵(Findingcameramatrices)部分学习这种计算方法,并且保留由该计算得到的带有小误差的特征对。使用光流进行点匹配一个替代使用丰富特征(如SURF)匹配的方法,是使用opticalflow(OF光流)进行匹配。下面的信息框提供了光流的一个简短的概述。最近的OpenCV为从两个图像获得流场扩展了API,并且现在更快,更强大。我们将尝试使用它作为匹配特征的一个替代品。【注释:光流是匹配来至一幅图像选择的点到另外一幅图像选着点的过程,假定这两个图像是一个视频序列的一部分并且它们彼此非常相近。大多数的光流方法比较一个小的区域,称为搜索窗口或者块,这些块围绕着图像A中的每一点和同样区域的图像B中的每一点。遵循计算机视觉中一个非常普通的规则,称为亮度恒定约束(brightnessconstancyconstraint)(和其他名字),图像中的这些小块从一个图像到另外一个图像不会有太大的变化,因此,他们的幅值差接近于0。除了匹配块,更新的光流方法使用一些额外的方法来获得更好的结果。其中一个方法就是使用图像金字塔,它是图像越来越小的尺寸(大小)版本,这考虑到了工作的从粗糙到精致——计算机视觉中一个非常有用的技巧。另外一个方法是定义一个流场上的全局约束,假定这些点相互靠近,向同一方向一起运动。在OpenCV中,一个更加深入的光流方法可以在ChapterDevelopingFluidWallUsingtheMicrosoftKinect中找到,这个书可以在出版社网站上访问到。】在OpenCV中,使用光流相当的简单,可以通过调用calcOpticalFlowPyrLK函数来实现。我们想要保存光流的结果匹配,像使用丰富的特征那样,在未来,我们希望两种方法可以互换。为了这个目标,我们必须安装一个特殊