结构来自运动来自多个视图
运动结构(SfM)是从一组二维视图中估计场景的三维结构的过程。它被用于许多应用,如机器人导航、自动驾驶和增强现实。这个例子向您展示了如何从一系列视图中估计校准相机的姿势,并重建场景的3-D结构,直到一个未知的比例因子。
概述
此示例演示如何从使用校准的相机拍摄的2-D视图序列重建3-D场景相机校准器.该示例使用imageviewset
对象来存储和管理与每个视图相关的数据,例如相机姿势和图像点,以及视图对中的点之间的匹配。
该示例使用成对点匹配来估计当前视图相对于前一个视图的相机姿态。方法将成对匹配链接到跨多个视图的较长的点轨道findTracks
方法imageviewset
对象。这些轨迹然后用作多视图三角测量的输入triangulateMultiview
功能和细化相机姿态和三维场景点使用bundleAdjustment
函数。
实例包括两个主要部分:摄像机运动估计和密集场景重建。在第一部分中,示例使用跨视图匹配的稀疏点集估计每个视图的相机姿势。在第二部分,示例再次遍历视图序列,使用愿景。PointTracker
跟踪视图中密集的点集,计算场景的密集三维重建。
摄像机运动估计算法包括以下步骤:
对于每一对连续的图像,找到一组对应的点。方法检测感兴趣的点
detectSURFFeatures
方法提取特征描述符extractFeatures
函数,并使用matchFeatures
函数。或者,您可以使用愿景。PointTracker
.估计当前视图的相对姿态,这是相对于前一个视图的相机方向和位置。这个例子使用了一个helper函数
helperEstimateRelativePose
,它调用estimateEssentialMatrix
而且estrelpose
.将当前视图的相对位姿转换为序列的第一个视图的坐标系。
存储当前视图属性:相机姿态和图像点。
存储先前视图和当前视图之间的inlier匹配。
查找到目前为止处理过的所有视图中的点轨迹。
使用
triangulateMultiview
函数计算轨道对应的初始三维位置。使用
bundleAdjustment
功能来完善相机的姿势和3-D点。存储精致的相机姿势imageviewset
对象。
读取输入图像序列
读取并显示图像序列。
使用|imageDatastore|获取目录中所有镜像文件名的列表。imageDir = fullfile(toolboxdir(“愿景”),“visiondata”,...“structureFromMotion”);imds = imageDatastore(imageDir);显示图片。图蒙太奇(imd)。文件,“大小”, [3,2]);将图像转换为灰度。images = cell(1, numel(imds.Files));为i = 1:numel(imds. files) i = readimage(imds, i);images{i} = im2gray(i);结束标题(“输入图像序列”);
摄像机参数
加载cameraParameters
对象创建。相机校准器.
data = load(fullfile(imageDir,“cameraParams.mat”));cameraParams = data.cameraParams;
创建一个包含第一个视图的视图集
使用一个imageviewset
对象来存储和管理与每个视图相关联的图像点和相机姿态,以及视图对之间的点匹配。一旦你填充了imageviewset
对象,您可以使用它在多个视图中查找点轨迹,并检索要使用的相机姿势triangulateMultiview
而且bundleAdjustment
功能。
获取相机的内在参数intrinsic = cameraparams . intrinsic;还原第一个图像。I = undistortion timage(图像{1},intrinsic);检测特征。增加“NumOctaves”有助于检测大规模%特征在高分辨率图像。使用ROI来消除虚假%的特征围绕图像的边缘。Border = 50;roi = [border, border, size(I, 2)- 2*border, size(I, 1)- 2*border];prevPoints = detectsurffatures (I, numocaves =8, ROI= ROI);提取特征。使用“直立”功能可以改善匹配,只要相机运动涉及很少或没有平面内旋转。。prevFeatures = extractFeatures(I, prevPoints,直立=true);创建一个空的imageviewset对象来管理与每个对象相关的数据%的观点。vSet = imageviewset;添加第一个视图。将相机与第一个视图相关联%和原点,沿z轴方向。viewId = 1;vSet = addView(vSet, viewId, rigidtform3d, Points=prevPoints);
添加其余的视图
看一下剩下的图像。对于每张图像
匹配前一个和当前图像之间的点。
估计当前视图相对于前一个视图的相机姿态。
在相对于第一个视图的全局坐标系中计算当前视图的相机姿态。
三角化最初的三维世界点。
使用束调整来细化所有相机姿势和3-D世界点。
为I = 2:数字(图像)还原当前图像。I = undistortion timage (images{I}, intrinsic);检测,提取和匹配特征。currPoints = detectsurffatures (I, numocaves =8, ROI= ROI);currFeatures = extractFeatures(I, currPoints,直立=true);indexPairs = matchFeatures(prevFeatures, currFeatures,...独特MaxRatio = 0.7, = true);选择匹配的点。matchedPoints1 = prevPoints(indexPairs(:, 1));matchedPoints2 = currPoints(indexPairs(:, 2));估计当前视图相对于前一个视图的相机姿态。%姿势是按比例计算的,这意味着之间的距离%前一个视图和当前视图中的摄像机的数量设置为1。这将在bundle调整中得到修正。[relPose, inlierIdx] = helperEstimateRelativePose(...matchedPoints1, matchedPoints2, intrinsic);获取包含前一个相机姿势的表。prevPose = pose (vSet, i-1).AbsolutePose;在全局坐标系中计算当前相机姿态%相对于第一个视图。currPose = rigidtform3d(prevpose . * relpose .);将当前视图添加到视图集中。vSet = addView(vSet, i, currPose, Points=currPoints);存储前一个视图和当前视图之间的匹配点。vSet = addConnection(vSet, i-1, i, relPose, Matches=indexPairs(inlierIdx,:));在所有视图中查找点轨迹。tracks = findTracks(vSet);获取包含所有视图的相机姿势的表。camposed = pose (vSet);三角化三维世界点的初始位置。。xyzPoints = triangulateMultiview(轨迹,campose, intrinsic);精炼3-D世界点和相机姿势。[xyzPoints, camposed, reprojectionErrors] = bundleadadjustment (xyzPoints, camposed, reprojectionErrors)...轨道,campose, intrinsic, FixedViewId=1,...PointsUndistorted = true);存储精致的相机姿势。vSet = updateView(vSet, camposed);prevFeatures = currFeatures;prevPoints = currPoints;结束
展示相机姿势
显示精致的相机姿势和3-D世界点。
显示相机姿势。camposed = pose (vSet);图;plotCamera(坎波斯,大小= 0.2);持有在排除有噪声的3-D点。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);标题(“精致的相机姿势”);
计算密集重建
再看一遍这些图片。这一次检测密集的角落集合,并在所有视图中使用愿景。PointTracker
.
读取并还原第一个图像I = undistortion timage(图像{1},intrinsic);检测第一个图像中的角落。prevPoints = detectMinEigenFeatures(I, MinQuality=0.001);创建点跟踪对象来跟踪视图中的点。追踪者=视觉。PointTracker(MaxBidirectionalError=1, NumPyramidLevels=6);初始化点跟踪器。prevPoints = prevPoints. location;初始化(跟踪器,prevPoints, I);在视图集中存储密集点。vSet = updateConnection(vSet, 1,2,匹配= 0 (0,2));vSet = updateView(vSet, 1, Points=prevPoints);跟踪所有视图中的点。为I = 2:数字(图像)读取并还原当前图像。I = undistortion timage (images{I}, intrinsic);跟踪分数。[currPoints, validIdx] = step(跟踪器,I);清除点之间的旧匹配。如果vSet = updateConnection(vSet, i, i+1,匹配= 0 (0,2));结束(vSet, i, Points=currPoints);将匹配的点存储在视图集中。matches = repmat((1:size(prevPoints, 1))', [1,2]);matches = matches(validIdx,:);vSet = updateConnection(vSet, i-1, i, Matches= Matches);结束在所有视图中查找点轨迹。tracks = findTracks(vSet);在所有视图中查找点轨迹。camposed = pose (vSet);三角化三维世界点的初始位置。。xyzPoints = triangulateMultiview(跟踪,campositions,...intrinsic);精炼3-D世界点和相机姿势。[xyzPoints, camposed, reprojectionErrors] = bundleadadjustment (...xyzPoints, tracks, campose, intrinsic, FixedViewId=1,...PointsUndistorted = true);
显示密集重建
显示相机姿势和密集的点云。
%显示精致的相机姿势。图;plotCamera(坎波斯,大小= 0.2);持有在排除有噪声的3-D世界点。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);标题(“密集的重建”);
参考文献
M.I.A. Lourakis和A.A. Argyros(2009)。SBA:通用稀疏束调整软件包。ACM数学软件学报(ACM) 36(1): 1-30。
R. Hartley, A. Zisserman,“计算机视觉中的多视图几何”,剑桥大学出版社,2003年。
[3] B. Triggs;p . McLauchlan;r·哈特利;A. Fitzgibbon(1999)。“束调整:现代综合”。视觉算法国际研讨会论文集。斯普林格出版社。298 - 372页。