Структура от движения (SfM) является процессом оценки 3-D структуры сцены из набора 2D представлений. Это используется во многих приложениях, таких как навигация робота, автономное управление и дополненная реальность. Этот пример показывает вам, как оценить положения калиброванной камеры от последовательности представлений и восстановить 3-D структуру сцены до неизвестного масштабного коэффициента.
Этот пример показывает, как восстановить 3-D сцену из последовательности 2D представлений, полученных с камерой, калиброванной с помощью приложения Camera Calibrator. Пример использует объект viewSet
сохранить и управлять данными, сопоставленными с каждым представлением, таким как положение камеры и точки изображений, а также соответствия между точками от пар представлений.
Пример использует попарные соответствия точки, чтобы оценить положение камеры текущего представления относительно предыдущего представления. Это затем соединяет попарные соответствия в более длинные дорожки точки, охватывающие несколько представлений с помощью метода findTracks
объекта viewSet
. Эти дорожки затем служат входными параметрами, чтобы мультипросмотреть триангуляцию с помощью функции triangulateMultiview
и улучшения положений камеры и 3-D точек сцены с помощью функции bundleAdjustment
.
Пример состоит из двух основных частей: оценка движения камеры и плотная реконструкция сцены. В первой части пример оценивает положение камеры для каждого представления с помощью разреженного набора точек, соответствующего через представления. Во второй части пример выполняет итерации по последовательности представлений снова, с помощью vision.PointTracker
, чтобы отследить плотный набор точек через представления, вычислить плотную 3-D реконструкцию сцены.
Алгоритм оценки движения камеры состоит из следующих шагов:
Для каждой пары последовательных изображений найдите набор соответствий точки. Этот пример обнаруживает точки интереса с помощью функции detectSURFFeatures
, извлекает дескрипторы функции с помощью функций extractFeatures
и находит соответствия с помощью функции matchFeatures
. Также можно отследить точки через представления с помощью vision.PointTracker
.
Оцените относительное положение текущего представления, которое является ориентацией камеры и местоположением относительно предыдущего представления. Пример использует функцию помощника helperEstimateRelativePose
, который вызывает estimateEssentialMatrix
и relativeCameraPose
.
Преобразуйте относительное положение текущего представления в систему координат первого представления последовательности.
Сохраните атрибуты текущего представления: положение камеры и точки изображений.
Сохраните соответствия inlier между предыдущим и текущим представлением.
Найдите дорожки точки через все представления обработанными до сих пор.
Используйте функцию triangulateMultiview
, чтобы вычислить начальные 3-D местоположения, соответствующие дорожкам.
Используйте функцию bundleAdjustment
, чтобы совершенствовать положения камеры и 3-D точки. Сохраните усовершенствованные положения камеры в объекте viewSet
.
Считайте и отобразите последовательность изображений.
% 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} = rgb2gray(I); end title('Input Image Sequence');
Загрузите созданное использование объекта cameraParameters
приложения Camera Calibrator.
load(fullfile(imageDir, 'cameraParams.mat'));
Используйте объект viewSet
сохранить и управлять точками изображений и положением камеры, сопоставленным с каждым представлением, а также соответствиями точки между парами представлений. Если вы заполняете объект viewSet
, можно использовать его, чтобы найти дорожки точки через несколько представлений и получить положения камеры, которые будут использоваться функциями bundleAdjustment
и triangulateMultiview
.
% Undistort the first image. I = undistortImage(images{1}, cameraParams); % 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 viewSet object to manage the data associated with each % view. vSet = viewSet; % 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, 'Points', prevPoints, 'Orientation', ... eye(3, 'like', prevPoints.Location), 'Location', ... zeros(1, 3, 'like', prevPoints.Location));
Пройдите остальную часть изображений. Для каждого изображения
Матч-пойнты между предыдущим и текущим изображением.
Оцените положение камеры текущего представления относительно предыдущего представления.
Вычислите положение камеры текущего представления в глобальной системе координат относительно первого представления.
Триангулируйте начальные 3-D мировые точки.
Используйте корректировку пакета, чтобы совершенствовать все положения камеры и 3-D мировые точки.
for i = 2:numel(images) % Undistort the current image. I = undistortImage(images{i}, cameraParams); % 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, cameraParams); % Add the current view to the view set. vSet = addView(vSet, i, 'Points', currPoints); % Store the point matches between the previous and the current views. vSet = addConnection(vSet, i-1, i, 'Matches', indexPairs(inlierIdx,:)); % Get the table containing the previous camera pose. prevPose = poses(vSet, i-1); prevOrientation = prevPose.Orientation{1}; prevLocation = prevPose.Location{1}; % Compute the current camera pose in the global coordinate system % relative to the first view. orientation = relativeOrient * prevOrientation; location = prevLocation + relativeLoc * prevOrientation; vSet = updateView(vSet, i, 'Orientation', orientation, ... 'Location', location); % 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, cameraParams); % Refine the 3-D world points and camera poses. [xyzPoints, camPoses, reprojectionErrors] = bundleAdjustment(xyzPoints, ... tracks, camPoses, cameraParams, '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.Location{1}; 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}, cameraParams); % 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}, cameraParams); % 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,... cameraParams); % Refine the 3-D world points and camera poses. [xyzPoints, camPoses, reprojectionErrors] = bundleAdjustment(... xyzPoints, tracks, camPoses, cameraParams, '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.Location{1}; 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). "SBA: пакет программного обеспечения для типичной разреженной корректировки пакета". Транзакции ACM на математическом программном обеспечении (ACM) 36 (1): 1-30.
[2] Р. Хартли, А. Зиссермен, "Несколько просматривают геометрию в компьютерном зрении", издательство Кембриджского университета, 2003.
[3] Б. Триггс; П. Маклочлан; Р. Хартли; А. Фицджиббон (1999). "Свяжите Корректировку: современный Синтез". Продолжения Международного семинара на Алгоритмах визуализации. Springer-Verlag. стр 298-372.