Main Content

두 시점에서의 움직임 기반 구조

움직임 기반 구조(SfM)는 2차원 영상 세트로부터 장면의 3차원 구조를 추정하는 과정입니다. 이 예제에서는 두 영상으로부터 보정된 카메라의 자세를 추정하고, 미지의 스케일링 인자로 장면의 3차원 구조를 복원한 후, 크기를 알고 있는 객체를 검출하여 실제 스케일링 인자를 복원하는 방법을 보여줍니다.

개요

이 예제에서는 카메라 보정기 앱을 사용하여 보정된 카메라로 촬영한 한 쌍의 2차원 영상에서 3차원 장면을 복원하는 방법을 보여줍니다. 이 알고리즘은 다음 단계로 구성됩니다.

  1. 두 영상 간에 희소한 점 집합을 매칭시킵니다. 두 영상 간의 대응점은 여러 방법으로 찾을 수 있습니다. 이 예제에서는 detectMinEigenFeatures 함수를 사용하여 첫 번째 영상에서 코너를 검출하고 vision.PointTracker를 사용하여 두 번째 영상에서 해당 코너를 추적합니다. 또는 extractFeatures를 사용한 후 matchFeatures를 사용할 수도 있습니다.

  2. estimateEssentialMatrix를 사용하여 Fundamental 행렬을 추정합니다.

  3. estrelpose 함수를 사용하여 카메라의 움직임을 계산합니다.

  4. 두 영상 사이에서 조밀한 점 집합을 매칭시킵니다. 'MinQuality'의 값을 줄인 상태로 detectMinEigenFeatures를 사용하여 해당 점을 다시 검출해서 더 많은 점을 구합니다. 그런 다음 vision.PointTracker를 사용하여 두 번째 영상에서 이 조밀한 점들을 추적합니다.

  5. triangulate를 사용하여 매칭되는 점들의 3차원 위치를 확인합니다.

  6. 크기를 알고 있는 객체를 검출합니다. 이 장면에는 반지름이 10cm인 지구본이 있습니다. pcfitsphere를 사용하여 포인트 클라우드에서 지구본을 찾습니다.

  7. 실제 스케일을 복구하여 메트릭을 복원합니다.

한 쌍의 영상 읽어 들이기

한 쌍의 영상을 작업 공간으로 불러옵니다.

imageDir = fullfile(toolboxdir('vision'),'visiondata','upToScaleReconstructionImages');
images = imageDatastore(imageDir);
I1 = readimage(images, 1);
I2 = readimage(images, 2);
figure
imshowpair(I1, I2, 'montage'); 
title('Original Images');

카메라 파라미터 불러오기

이 예제에서는 카메라 보정기 앱에서 계산된 카메라 파라미터를 사용합니다. 이 파라미터는 cameraIntrinsics 객체에 저장되며, 카메라 내부 파라미터와 렌즈 왜곡 계수를 포함합니다.

% Load precomputed camera intrinsics
data = load('sfmCameraIntrinsics.mat');
intrinsics = data.intrinsics;

렌즈 왜곡 제거하기

렌즈 왜곡은 최종 복원의 정확도에 영향을 줄 수 있습니다. undistortImage 함수를 사용하여 각 영상에서 왜곡을 제거할 수 있습니다. 이 과정에서는 렌즈의 방사형 왜곡으로 인해 휘어진 선을 곧게 만듭니다.

I1 = undistortImage(I1, intrinsics);
I2 = undistortImage(I2, intrinsics);
figure 
imshowpair(I1, I2, 'montage');
title('Undistorted Images');

영상 간의 대응점 찾기

추적하기 좋은 특징을 검출합니다. 'MinQuality'의 값을 줄여서 더 적은 수의 점을 검출합니다. 이 경우 점들이 영상 전체에 더 균일하게 분포하게 됩니다. 카메라의 움직임이 그다지 크지 않은 경우 대응점을 결정하는 좋은 방법은 KLT 알고리즘을 사용하는 것입니다.

% Detect feature points
imagePoints1 = detectMinEigenFeatures(im2gray(I1), MinQuality = 0.1);

% Visualize detected points
figure
imshow(I1, InitialMagnification = 50);
title('150 Strongest Corners from the First Image');
hold on
plot(selectStrongest(imagePoints1, 150));

% Create the point tracker
tracker = vision.PointTracker(MaxBidirectionalError=1, NumPyramidLevels=5);

% Initialize the point tracker
imagePoints1 = imagePoints1.Location;
initialize(tracker, imagePoints1, I1);

% Track the points
[imagePoints2, validIdx] = step(tracker, I2);
matchedPoints1 = imagePoints1(validIdx, :);
matchedPoints2 = imagePoints2(validIdx, :);

% Visualize correspondences
figure
showMatchedFeatures(I1, I2, matchedPoints1, matchedPoints2);
title('Tracked Features');

Essential 행렬 추정하기

estimateEssentialMatrix 함수를 사용하여 Essential 행렬을 계산하고 에피폴라 제약 조건을 충족하는 정상값 점을 찾습니다.

% Estimate the fundamental matrix
[E, epipolarInliers] = estimateEssentialMatrix(...
    matchedPoints1, matchedPoints2, intrinsics, Confidence = 99.99);

% Find epipolar inliers
inlierPoints1 = matchedPoints1(epipolarInliers, :);
inlierPoints2 = matchedPoints2(epipolarInliers, :);

% Display inlier matches
figure
showMatchedFeatures(I1, I2, inlierPoints1, inlierPoints2);
title('Epipolar Inliers');

카메라 자세 계산하기

첫 번째 카메라를 기준으로 두 번째 카메라의 위치와 방향을 계산합니다. 참고로, 평행 이동은 스케일로만(up to scale) 계산될 수 있기 때문에 loc는 평행 이동 단위 벡터입니다.

relPose = estrelpose(E, intrinsics, inlierPoints1, inlierPoints2);

매칭되는 점들의 3차원 위치 복원하기

더 낮은 'MinQuality'로 첫 번째 영상에서 점들을 다시 검출하여 더 많은 점을 구합니다. 이 새로운 점들을 두 번째 영상에서 추적합니다. DLT(Direct Linear Transformation) 알고리즘[1]을 구현하는 triangulate 함수를 사용하여, 매칭되는 점들에 대응하는 3차원 위치를 추정합니다. 원점을 첫 번째 영상 카메라의 광학적 중심에 배치합니다.

% Detect dense feature points. Use an ROI to exclude points close to the
% image edges.
border = 30;
roi = [border, border, size(I1, 2)- 2*border, size(I1, 1)- 2*border];
imagePoints1 = detectMinEigenFeatures(im2gray(I1), ROI = roi, ...
    MinQuality = 0.001);

% Create the point tracker
tracker = vision.PointTracker(MaxBidirectionalError=1, NumPyramidLevels=5);

% Initialize the point tracker
imagePoints1 = imagePoints1.Location;
initialize(tracker, imagePoints1, I1);

% Track the points
[imagePoints2, validIdx] = step(tracker, I2);
matchedPoints1 = imagePoints1(validIdx, :);
matchedPoints2 = imagePoints2(validIdx, :);

% Compute the camera matrices for each position of the camera
% The first camera is at the origin looking along the Z-axis. Thus, its
% transformation is identity.
camMatrix1 = cameraProjection(intrinsics, rigidtform3d);
camMatrix2 = cameraProjection(intrinsics, pose2extr(relPose));

% Compute the 3-D points
points3D = triangulate(matchedPoints1, matchedPoints2, camMatrix1, camMatrix2);

% Get the color of each reconstructed point
numPixels = size(I1, 1) * size(I1, 2);
allColors = reshape(I1, [numPixels, 3]);
colorIdx = sub2ind([size(I1, 1), size(I1, 2)], round(matchedPoints1(:,2)), ...
    round(matchedPoints1(:, 1)));
color = allColors(colorIdx, :);

% Create the point cloud
ptCloud = pointCloud(points3D, 'Color', color);

3차원 포인트 클라우드 표시하기

plotCamera 함수를 사용하여 카메라의 위치와 방향을 시각화하고 pcshow 함수를 사용하여 포인트 클라우드를 시각화합니다.

% Visualize the camera locations and orientations
cameraSize = 0.3;
figure
plotCamera(Size=cameraSize, Color='r', Label='1', Opacity=0);
hold on
grid on
plotCamera(AbsolutePose=relPose, Size=cameraSize, ...
    Color='b', Label='2', Opacity=0);

% Visualize the point cloud
pcshow(ptCloud, VerticalAxis='y', VerticalAxisDir='down', MarkerSize=45);

% Rotate and zoom the plot
camorbit(0, -30);
camzoom(1.5);

% Label the axes
xlabel('x-axis');
ylabel('y-axis');
zlabel('z-axis')

title('Up to Scale Reconstruction of the Scene');

구를 포인트 클라우드에 피팅하여 지구본 찾기

pcfitsphere 함수를 사용하여 구를 3차원 점에 피팅하여 포인트 클라우드에서 지구본을 찾습니다.

% Detect the globe
globe = pcfitsphere(ptCloud, 0.1);

% Display the surface of the globe
plot(globe);
title('Estimated Location and Size of the Globe');
hold off

장면의 메트릭 복원

지구본의 실제 반지름은 10cm입니다. 이제 센티미터 단위로 3차원 점의 좌표를 확인할 수 있습니다.

% Determine the scale factor
scaleFactor = 10 / globe.Radius;

% Scale the point cloud
ptCloud = pointCloud(points3D * scaleFactor, Color=color);
relPose.Translation = relPose.Translation * scaleFactor;

% Visualize the point cloud in centimeters
cameraSize = 2; 
figure
plotCamera(Size=cameraSize, Color='r', Label='1', Opacity=0);
hold on
grid on
plotCamera(AbsolutePose=relPose, Size=cameraSize, ...
    Color='b', Label='2', Opacity=0);

% Visualize the point cloud
pcshow(ptCloud, VerticalAxis='y', VerticalAxisDir='down', MarkerSize=45);
camorbit(0, -30);
camzoom(1.5);

% Label the axes
xlabel('x-axis (cm)');
ylabel('y-axis (cm)');
zlabel('z-axis (cm)')
title('Metric Reconstruction of the Scene');

요약

이 예제에서는 보정된 카메라로 촬영한 영상 2개에서 카메라 움직임을 복구하고 장면의 3차원 구조를 복원하는 방법을 다루었습니다.

참고 문헌

[1] Hartley, Richard, and Andrew Zisserman. Multiple View Geometry in Computer Vision. Second Edition. Cambridge, 2000.