Структура от движения от нескольких представлений

Структура от движения (SfM) является процессом оценки 3-D структуры сцены из набора 2D представлений. Это используется во многих приложениях, таких как навигация робота, автономное управление и дополненная реальность. Этот пример показывает вам, как оценить положения калиброванной камеры от последовательности представлений и восстановить 3-D структуру сцены до неизвестного масштабного коэффициента.

Обзор

Этот пример показывает, как восстановить 3-D сцену из последовательности 2D представлений, полученных с камерой, калиброванной с помощью приложения Camera Calibrator. Пример использует объект viewSet сохранить и управлять данными, сопоставленными с каждым представлением, таким как положение камеры и точки изображений, а также соответствия между точками от пар представлений.

Пример использует попарные соответствия точки, чтобы оценить положение камеры текущего представления относительно предыдущего представления. Это затем соединяет попарные соответствия в более длинные дорожки точки, охватывающие несколько представлений с помощью метода findTracks объекта viewSet. Эти дорожки затем служат входными параметрами, чтобы мультипросмотреть триангуляцию с помощью функции triangulateMultiview и улучшения положений камеры и 3-D точек сцены с помощью функции bundleAdjustment.

Пример состоит из двух основных частей: оценка движения камеры и плотная реконструкция сцены. В первой части пример оценивает положение камеры для каждого представления с помощью разреженного набора точек, соответствующего через представления. Во второй части пример выполняет итерации по последовательности представлений снова, с помощью vision.PointTracker, чтобы отследить плотный набор точек через представления, вычислить плотную 3-D реконструкцию сцены.

Алгоритм оценки движения камеры состоит из следующих шагов:

  1. Для каждой пары последовательных изображений найдите набор соответствий точки. Этот пример обнаруживает точки интереса с помощью функции detectSURFFeatures, извлекает дескрипторы функции с помощью функций extractFeatures и находит соответствия с помощью функции matchFeatures. Также можно отследить точки через представления с помощью vision.PointTracker.

  2. Оцените относительное положение текущего представления, которое является ориентацией камеры и местоположением относительно предыдущего представления. Пример использует функцию помощника helperEstimateRelativePose, который вызывает estimateEssentialMatrix и relativeCameraPose.

  3. Преобразуйте относительное положение текущего представления в систему координат первого представления последовательности.

  4. Сохраните атрибуты текущего представления: положение камеры и точки изображений.

  5. Сохраните соответствия inlier между предыдущим и текущим представлением.

  6. Найдите дорожки точки через все представления обработанными до сих пор.

  7. Используйте функцию triangulateMultiview, чтобы вычислить начальные 3-D местоположения, соответствующие дорожкам.

  8. Используйте функцию 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));

Добавьте остальную часть представлений

Пройдите остальную часть изображений. Для каждого изображения

  1. Матч-пойнты между предыдущим и текущим изображением.

  2. Оцените положение камеры текущего представления относительно предыдущего представления.

  3. Вычислите положение камеры текущего представления в глобальной системе координат относительно первого представления.

  4. Триангулируйте начальные 3-D мировые точки.

  5. Используйте корректировку пакета, чтобы совершенствовать все положения камеры и 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.