主要内容

结构从多视图运动

来自运动的结构(SfM)是从一组2-D视图估计场景的3-D结构的过程。它被用于许多应用,如机器人导航、自动驾驶和增强现实。这个例子向您展示了如何从一系列视图中估计校准相机的姿态,并重建场景的3-D结构,直到未知的比例因子。

概述

这个例子展示了如何从一个2d视图序列重建一个3d场景,使用校准的相机相机校准器.这个例子使用了imageviewset对象来存储和管理与每个视图相关联的数据,如摄像机姿态和图像点,以及视图对中的点之间的匹配。

该示例使用成对点匹配来估计当前视图相对于前一个视图的摄像机姿态。然后,它将成对匹配链接到跨越多个视图的更长的点跟踪findTracks的方法imageviewset对象。这些轨道然后作为输入多视图三角测量使用triangulateMultiview函数,以及相机姿态和三维场景点的细化bundleAdjustment函数。

该实例包括两个主要部分:摄像机运动估计和密集场景重建。在第一部分中,示例使用一个稀疏的点集来估计每个视图的相机姿态。在第二部分中,示例再次遍历视图序列,使用愿景。PointTracker跟踪视图中密集的点集,计算场景的密集三维重建。

摄像机运动估计算法由以下步骤组成:

  1. 对于每一对连续的图像,找到一组对应的点。方法检测兴趣点detectSURFFeatures函数提取特征描述符extractFeatures函数,并使用matchFeatures函数。或者,您可以使用愿景。PointTracker

  2. 估计当前视图的相对姿态,这是摄像机相对于前一个视图的方向和位置。这个例子使用了一个辅助函数helperEstimateRelativePose,电话estimateEssentialMatrixrelativeCameraPose

  3. 将当前视图的相对姿态转换为序列的第一个视图的坐标系统。

  4. 存储当前视图属性:相机姿态和图像点。

  5. 存储前一个视图和当前视图之间的inlier匹配。

  6. 查找到目前为止处理过的所有视图的点轨迹。

  7. 使用triangulateMultiview函数来计算轨道对应的初始三维位置。

  8. 使用bundleAdjustment功能来改进相机的姿态和三维点。将精致的相机姿势储存在imageviewset对象。

读取输入图像序列

读取并显示图像序列。

%使用|imageDatastore|获取所有图像文件名的列表%目录。imageDir = fullfile (toolboxdir (“愿景”),“visiondata”...“structureFromMotion”);imd = imageDatastore (imageDir);%显示图像。图蒙太奇(imd)。文件,“大小”, 3, 2);%将图像转换为灰度。images = cell(1, numel(imds.Files));i = 1:numel(imds. files) i = readimage(imds, i);{我}= im2gray图像(i);结束标题(输入图像序列的);

图中包含一个坐标轴。标题为Input Image Sequence的轴包含一个Image类型的对象。

负载相机参数

加载cameraParameters使用相机校准器

data =加载(fullfile (imageDir“cameraParams.mat”));cameraParams = data.cameraParams;

创建包含第一个视图的视图集

使用一个imageviewset对象来存储和管理与每个视图相关联的图像点和摄像机姿态,以及视图对之间的点匹配。一旦你填充了imageviewset对象,你可以使用它在多个视图中找到点轨迹和检索相机的姿态要使用triangulateMultiviewbundleAdjustment功能。

%获取相机的内在参数intrinsic = cameraParams.Intrinsics;不失真的第一个图像。I = undistortion timage (images{1}, intrinsics);%检测功能。增加“NumOctaves”有助于检测大规模%的特征在高分辨率的图像。使用ROI来消除虚假%特征围绕图像的边缘。边境= 50;roi = [border, border, size(I, 2)- 2*border, size(I, 1)- 2*border];prevPoints = detectSURFFeatures(我“NumOctaves”8“投资回报”roi);%提取特征。使用“直立”功能可以提高匹配,只要相机的运动很少或没有涉及到面内旋转。prevFeatures = extractFeatures(I, prevPoints,“正直”,真正的);%创建一个空的imageviewset对象来管理与每个对象关联的数据%的观点。vSet = imageviewset;%添加第一个视图。放置与第一个视图相关联的摄像机%和原点,沿z轴方向。viewId = 1;vSet = addView(vSet, viewId, rigid3d,“点”, prevPoints);

添加其余的视图

再看一遍剩下的图片。为每一个图像

  1. 先前图像和当前图像之间的匹配点。

  2. 估计当前视图相对于前一个视图的摄像机姿态。

  3. 相对于第一个视图,计算当前视图在全局坐标系中的摄像机姿态。

  4. 对初始三维世界点进行三角定位。

  5. 使用束调整来完善所有的相机姿态和3d世界点。

我= 2:元素个数(图片)%恢复当前图像的失真。I = undistortion timage (images{I}, intrinsics);检测、提取和匹配特征。currPoints = detectSURFFeatures(我“NumOctaves”8“投资回报”roi);currPoints = extractFeatures(I, currPoints,“正直”,真正的);indexPairs = matchFeatures(prevFeatures, currFeatures,...“MaxRatio”7,“独特的”,真正的);%选择匹配点。matchedpoint1 = prevPoints(indexPairs(:, 1));matchedpoint2 = currPoints(indexPairs(:, 2));估算当前视图相对于前一个视图的相机姿态。姿势是按比例计算的,这意味着两者之间的距离%前一个视图和当前视图的摄像机设置为1。%这将被bundle调整修正。[relativeOrient, relativeLoc, inlierIdx] = helperEstimateRelativePose(...matchedPoints1、matchedPoints2 intrinsic);获取包含先前摄像机姿态的表格。prevPose = pose (vSet, i-1).AbsolutePose;relPose = rigid3d(relativeOrient, relativeLoc);在全局坐标系中计算当前摄像机的姿态%相对于第一个视图。currPose = rigid3d (relPose。T * prevPose.T);将当前视图添加到视图集。vSet = addView(vSet, i, currPose,“点”, currPoints);%存储前一个视图和当前视图之间的点匹配。vSet = addConnection(vSet, i-1, i, relPose,“匹配”indexPairs (inlierIdx:));在所有视图中找到点轨迹。跟踪= findTracks (vSet);%获取包含所有视图的相机姿势的表格。坎波斯=姿势(vSet);三角定位三维世界点的初始位置。xyzPoints = triangulateMultiview(tracks, campose, intrinsics);改进3d世界点和相机姿势。[xyzPoints, campose, reprojectionErrors] = bundleAdjustment(xyzPoints,...intrinsic痕迹,坎波斯,“FixedViewId”, 1...“PointsUndistorted”,真正的);存储精致的相机姿势。vSet = updateView(vSet, camPoses);prevFeatures = currFeatures;prevPoints = currPoints;结束

显示相机的姿势

展示精致的相机姿势和三维世界点。

显示相机姿势。坎波斯=姿势(vSet);图;plotCamera(坎波斯,“大小”, 0.2);持有排除有噪声的三维点。goodIdx = (reprojectionErrors < 5);xyzPoints = xyzPoints(goodIdx,:);%显示3d点。pcshow (xyzPoints“VerticalAxis”“y”“VerticalAxisDir”“下来”...“MarkerSize”, 45岁);网格持有%指定查看音量。loc1 = camPoses.AbsolutePose (1) .Translation;xlim ([loc1 (1) 5, loc1 (1) + 4]);ylim ([loc1 (2) 5, loc1 (2) + 4]);zlim ([loc1 (3) 1, loc1 (3) + 20));camorbit (0, -30);标题(“精制相机姿势”);

图中包含一个坐标轴。标题为“精致相机姿态”的轴包含51个类型为线、文本、补丁、散点的对象。

计算密集的重建

再浏览一遍这些图像。这一次检测密集的角集,并在所有视图中使用愿景。PointTracker

读取并校正第一个图像I = undistortion timage (images{1}, intrinsics);%检测第一个图像中的角。prevPoints = detectMinEigenFeatures(我“MinQuality”, 0.001);创建点跟踪器对象来跨视图跟踪点。追踪=愿景。PointTracker (“MaxBidirectionalError”, 1“NumPyramidLevels”6);初始化点跟踪器。prevPoints = prevPoints.Location;初始化(prevPoints追踪,我);%在视图集中存储密集点。vSet = updateConnection(vSet, 1, 2,“匹配”, 0 (0, 2));vSet = updateView(vSet, 1,)“点”, prevPoints);%在所有视图中跟踪点。我= 2:元素个数(图片)读取并消除当前图像的失真。I = undistortion timage (images{I}, intrinsics);%追踪积分。[currPoints, valididdx] = step(tracker, I);%清除旧的匹配点之间。如果i < numel(images) vSet = updateConnection(vSet, i, i+1,)“匹配”, 0 (0, 2));结束vSet = updateView(vSet, i,“点”, currPoints);%在视图集中存储点匹配。match = repmat((1:size(prevPoints, 1))', [1,2]);match = match (valididdx,:);vSet = updateConnection(vSet, i-1, i,“匹配”,匹配);结束在所有视图中找到点轨迹。跟踪= findTracks (vSet);在所有视图中找到点轨迹。坎波斯=姿势(vSet);三角定位三维世界点的初始位置。xyzPoints = triangulateMultiview(轨道,campose,...intrinsic);改进3d世界点和相机姿势。[xyzPoints, campose, reprojectionErrors] = bundleAdjustment(...xyzPoints, tracks, campose, intrinsics,“FixedViewId”, 1...“PointsUndistorted”,真正的);

显示密度重建

显示相机的姿态和密集的点云。

显示精致的相机姿势。图;plotCamera(坎波斯,“大小”, 0.2);持有不包括嘈杂的三维世界点。goodIdx = (reprojectionErrors < 5);%显示密集的三维世界点。pcshow (xyzPoints (goodIdx:)“VerticalAxis”“y”“VerticalAxisDir”“下来”...“MarkerSize”, 45岁);网格持有%指定查看音量。loc1 = camPoses.AbsolutePose (1) .Translation;xlim ([loc1 (1) 5, loc1 (1) + 4]);ylim ([loc1 (2) 5, loc1 (2) + 4]);zlim ([loc1 (3) 1, loc1 (3) + 20));camorbit (0, -30);标题(“密集的重建”);

图中包含一个坐标轴。标题为“密集重建”的轴包含51个类型为line, text, patch, scatter的对象。

参考文献

M.I.A. Lourakis和A.A. Argyros(2009)。SBA:通用稀疏束调整软件包。数学软件学报36(1):1-30。

[2] R. Hartley, A. Zisserman,“计算机视觉中的多视图几何”,剑桥大学出版社,2003。

[3] b区格;p . McLauchlan;r·哈特利;菲茨吉本(1999)。《Bundle Adjustment: A Modern Synthesis》。国际视觉算法研讨会论文集。斯普林格出版社。298 - 372页。