Структура от движения (SfM) является процессом оценки 3-D структуры сцены из набора 2-D представлений. Он используется во многих приложениях, таких как навигация по роботу, автономное управление автомобилем и дополненная реальность. В этом примере показано, как оценить положения калиброванной камеры из последовательности видов и восстановить 3-D структуру сцены до неизвестного масштабного коэффициента.
В этом примере показано, как восстановить 3-D сцену из последовательности 2-D видов, взятых с камерой, калиброванной с помощью Camera Calibrator. В примере используется imageviewset
объект для хранения и управления данными, связанными с каждым видом, таким как положение камеры и точки изображения, а также для соответствия между точками из пар представлений.
Пример использует парные совпадения точек, чтобы оценить положение камеры текущего вида относительно предыдущего вида. Затем он связывает парные совпадения с более длинными точечными треками, охватывающими несколько видов, используя findTracks
метод imageviewset
объект. Затем эти треки служат входами для мультивидовой триангуляции с использованием triangulateMultiview
функция и уточнение положений камеры и 3-D точек сцены с помощью bundleAdjustment
функция.
Пример состоит из двух основных частей: оценка движения камеры и плотная реконструкция сцены. В первой части пример оценивает положение камеры для каждого вида, используя разреженный набор точек, совпадающих между видами. Во второй части пример повторяет итерацию последовательности представлений, используя vision.PointTracker
для отслеживания плотного набора точек на всех видах, для вычисления плотного 3-D восстановления сцены.
Алгоритм оценки движения камеры состоит из следующих шагов:
Для каждой пары последовательных изображений найдите набор соответствий точек. Этот пример обнаруживает процентные точки, используя detectSURFFeatures
function, извлекает дескрипторы функций с помощью extractFeatures
functions, и находит совпадения используя matchFeatures
функция. Кроме того, можно отслеживать точки между видами с помощью vision.PointTracker
.
Оцените относительное положение текущего вида, которое является ориентацией камеры и ее местоположением относительно предыдущего вида. В примере используется вспомогательная функция helperEstimateRelativePose
, который вызывает estimateEssentialMatrix
и relativeCameraPose
.
Преобразуйте относительное положение текущего вида в систему координат первого вида последовательности.
Сохраните текущие атрибуты представления: положение камеры и точки изображения.
Сохраните соответствие inlier между предыдущим и текущим представлениями.
Поиск треков точек по всем видам, обработанным до сих пор.
Используйте 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
объект, созданный с помощью Camera Calibrator.
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). «SBA: программный пакет для типовой регулировки разреженного пакета». Транзакции ACM на математическом программном обеспечении (ACM) 36 (1): 1-30.
[2] R. Hartley, A. Zisserman, «Multiple View Geometry in Компьютерное Зрение», Cambridge University Press, 2003.
[3] Б. Триггеры; П. Маклаучлан; Р. Хартли; А. Фицгиббон (1999). Bundle Adjustment: A Modern Synthesis (неопр.) (недоступная ссылка). Материалы Международного практикума по алгоритмам видения. Springer-Verlag. с. 298-372.