Структура из движения (SfM) - процесс оценки 3-D структуры сцены по набору 2-D видов. Он используется во многих приложениях, таких как навигация роботов, автономное вождение и дополненная реальность. В этом примере показано, как оценить позы калиброванной камеры по последовательности видов и восстановить 3-D структуру сцены до неизвестного масштабного коэффициента.
В этом примере показано, как реконструировать сцену 3-D из последовательности видов 2-D, взятых с помощью камеры, откалиброванной с помощью калибратора камеры. В примере используется imageviewset объект для хранения и управления данными, связанными с каждым видом, например позицией камеры и точками изображения, а также совпадениями между точками из пар видов.
В примере для оценки положения камеры текущего вида относительно предыдущего вида используются парные совпадения точек. Затем он связывает парные совпадения в более длинные дорожки точек, охватывающие несколько видов, используя findTracks способ imageviewset объект. Эти дорожки затем служат в качестве входных данных для многозначной триангуляции с использованием triangulateMultiview функция и уточнение поз камеры и точек сцены 3-D с помощью bundleAdjustment функция.
Пример состоит из двух основных частей: оценка движения камеры и плотная реконструкция сцены. В первой части пример оценивает положение камеры для каждого вида с использованием разреженного набора точек, совпадающих по видам. Во второй части пример повторяется по последовательности представлений, используя vision.PointTrackerотслеживать плотный набор точек по видам, вычислять плотную 3-D реконструкцию сцены.
Алгоритм оценки движения камеры состоит из следующих этапов:
Для каждой пары последовательных изображений найдите набор соответствий точек. В этом примере определяются процентные баллы с помощью detectSURFFeatures извлекает дескрипторы функций с помощью extractFeatures и находит совпадения с помощью matchFeatures функция. Можно также отслеживать точки на видах с помощью vision.PointTracker.
Оценка относительного положения текущего вида, которое является ориентацией камеры и расположением относительно предыдущего вида. В примере используется вспомогательная функция helperEstimateRelativePose, который звонит estimateEssentialMatrix и relativeCameraPose.
Преобразование относительного положения текущего вида в систему координат первого вида последовательности.
Сохранение текущих атрибутов вида: позы камеры и точек изображения.
Храните инкрементные совпадения между предыдущим и текущим представлением.
Поиск дорожек точек по всем видам, обработанным до сих пор.
Используйте triangulateMultiview функция для вычисления начальных местоположений 3-D, соответствующих дорожкам.
Используйте bundleAdjustment функция для уточнения поз камеры и точек 3-D. Хранить улучшенные позы камеры в imageviewset объект.
Считывание и отображение последовательности изображений.
% Use |imageDatastore| to get a list of all image file names in a % directory. imageDir = fullfile(toolboxdir('vision'), 'visiondata', ... 'structureFromMotion'); imds = imageDatastore(imageDir); % Display the images. figure montage(imds.Files, 'Size', [3, 2]); % Convert the images to grayscale. images = cell(1, numel(imds.Files)); for i = 1:numel(imds.Files) I = readimage(imds, i); images{i} = im2gray(I); end title('Input Image Sequence');

Загрузить cameraParameters объект, созданный с помощью калибратора камеры.
data = load(fullfile(imageDir, 'cameraParams.mat'));
cameraParams = data.cameraParams;Использовать imageviewset объект для хранения и управления точками изображения и позицией камеры, связанной с каждым видом, а также совпадениями точек между парами видов. После заполнения imageviewset объект, его можно использовать для поиска дорожек точек на нескольких видах и извлечения поз камеры, которые будут использоваться triangulateMultiview и bundleAdjustment функции.
% Get intrinsic parameters of the camera intrinsics = cameraParams.Intrinsics; % Undistort the first image. I = undistortImage(images{1}, intrinsics); % Detect features. Increasing 'NumOctaves' helps detect large-scale % features in high-resolution images. Use an ROI to eliminate spurious % features around the edges of the image. border = 50; roi = [border, border, size(I, 2)- 2*border, size(I, 1)- 2*border]; prevPoints = detectSURFFeatures(I, 'NumOctaves', 8, 'ROI', roi); % Extract features. Using 'Upright' features improves matching, as long as % the camera motion involves little or no in-plane rotation. prevFeatures = extractFeatures(I, prevPoints, 'Upright', true); % Create an empty imageviewset object to manage the data associated with each % view. vSet = imageviewset; % Add the first view. Place the camera associated with the first view % and the origin, oriented along the Z-axis. viewId = 1; vSet = addView(vSet, viewId, rigid3d, 'Points', prevPoints);
Пройдитесь по остальным изображениям. Для каждого изображения
Совпадение точек между предыдущим и текущим изображением.
Оценка положения камеры текущего вида относительно предыдущего вида.
Вычислите положение камеры текущего вида в глобальной системе координат относительно первого вида.
Триангулировать начальные точки мира 3-D.
Используйте регулировку пучка для уточнения всех поз камеры и точек 3-D мира.
for i = 2:numel(images) % Undistort the current image. I = undistortImage(images{i}, intrinsics); % Detect, extract and match features. currPoints = detectSURFFeatures(I, 'NumOctaves', 8, 'ROI', roi); currFeatures = extractFeatures(I, currPoints, 'Upright', true); indexPairs = matchFeatures(prevFeatures, currFeatures, ... 'MaxRatio', .7, 'Unique', true); % Select matched points. matchedPoints1 = prevPoints(indexPairs(:, 1)); matchedPoints2 = currPoints(indexPairs(:, 2)); % Estimate the camera pose of current view relative to the previous view. % The pose is computed up to scale, meaning that the distance between % the cameras in the previous view and the current view is set to 1. % This will be corrected by the bundle adjustment. [relativeOrient, relativeLoc, inlierIdx] = helperEstimateRelativePose(... matchedPoints1, matchedPoints2, intrinsics); % Get the table containing the previous camera pose. prevPose = poses(vSet, i-1).AbsolutePose; relPose = rigid3d(relativeOrient, relativeLoc); % Compute the current camera pose in the global coordinate system % relative to the first view. currPose = rigid3d(relPose.T * prevPose.T); % Add the current view to the view set. vSet = addView(vSet, i, currPose, 'Points', currPoints); % Store the point matches between the previous and the current views. vSet = addConnection(vSet, i-1, i, relPose, 'Matches', indexPairs(inlierIdx,:)); % Find point tracks across all views. tracks = findTracks(vSet); % Get the table containing camera poses for all views. camPoses = poses(vSet); % Triangulate initial locations for the 3-D world points. xyzPoints = triangulateMultiview(tracks, camPoses, intrinsics); % Refine the 3-D world points and camera poses. [xyzPoints, camPoses, reprojectionErrors] = bundleAdjustment(xyzPoints, ... tracks, camPoses, intrinsics, 'FixedViewId', 1, ... 'PointsUndistorted', true); % Store the refined camera poses. vSet = updateView(vSet, camPoses); prevFeatures = currFeatures; prevPoints = currPoints; end
Отображение улучшенных поз камеры и 3-D точек мира.
% Display camera poses. camPoses = poses(vSet); figure; plotCamera(camPoses, 'Size', 0.2); hold on % Exclude noisy 3-D points. goodIdx = (reprojectionErrors < 5); xyzPoints = xyzPoints(goodIdx, :); % Display the 3-D points. pcshow(xyzPoints, 'VerticalAxis', 'y', 'VerticalAxisDir', 'down', ... 'MarkerSize', 45); grid on hold off % Specify the viewing volume. 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); title('Refined Camera Poses');

Пройдитесь по изображениям еще раз. На этот раз определите плотный набор углов и отслеживайте их по всем видам с помощьюvision.PointTracker.
% Read and undistort the first image I = undistortImage(images{1}, intrinsics); % Detect corners in the first image. prevPoints = detectMinEigenFeatures(I, 'MinQuality', 0.001); % Create the point tracker object to track the points across views. tracker = vision.PointTracker('MaxBidirectionalError', 1, 'NumPyramidLevels', 6); % Initialize the point tracker. prevPoints = prevPoints.Location; initialize(tracker, prevPoints, I); % Store the dense points in the view set. vSet = updateConnection(vSet, 1, 2, 'Matches', zeros(0, 2)); vSet = updateView(vSet, 1, 'Points', prevPoints); % Track the points across all views. for i = 2:numel(images) % Read and undistort the current image. I = undistortImage(images{i}, intrinsics); % Track the points. [currPoints, validIdx] = step(tracker, I); % Clear the old matches between the points. if i < numel(images) vSet = updateConnection(vSet, i, i+1, 'Matches', zeros(0, 2)); end vSet = updateView(vSet, i, 'Points', currPoints); % Store the point matches in the view set. matches = repmat((1:size(prevPoints, 1))', [1, 2]); matches = matches(validIdx, :); vSet = updateConnection(vSet, i-1, i, 'Matches', matches); end % Find point tracks across all views. tracks = findTracks(vSet); % Find point tracks across all views. camPoses = poses(vSet); % Triangulate initial locations for the 3-D world points. xyzPoints = triangulateMultiview(tracks, camPoses,... intrinsics); % Refine the 3-D world points and camera poses. [xyzPoints, camPoses, reprojectionErrors] = bundleAdjustment(... xyzPoints, tracks, camPoses, intrinsics, 'FixedViewId', 1, ... 'PointsUndistorted', true);
Отображение поз камеры и плотного облака точек.
% Display the refined camera poses. figure; plotCamera(camPoses, 'Size', 0.2); hold on % Exclude noisy 3-D world points. goodIdx = (reprojectionErrors < 5); % Display the dense 3-D world points. pcshow(xyzPoints(goodIdx, :), 'VerticalAxis', 'y', 'VerticalAxisDir', 'down', ... 'MarkerSize', 45); grid on hold off % Specify the viewing volume. 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); title('Dense Reconstruction');

[1] M.I.A. Луракис и А. А. Аргирос (2009). «Преимущества для малого бизнеса: пакет программного обеспечения для настройки общего разреженного пакета». Транзакции ACM по математическому программному обеспечению (ACM) 36 (1): 1-30.
[2] Р. Хартли, А. Зиссерман, «Геометрия множественного вида в компьютерном зрении», Cambridge University Press, 2003.
[3] B. Триггеры; П. Маклаухлан; Р. Хартли; А. Фицгиббон (1999). «Корректировка пучка: современный синтез». Материалы Международного рабочего совещания по алгоритмам видения. Спрингер-Верлаг. стр. 298-372.