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');

Figure contains an axes object. The axes object with title Original Images contains an object of type image.

카메라 파라미터 불러오기

이 예제에서는 카메라 보정기 앱에서 계산된 카메라 파라미터를 사용합니다. 이 파라미터는 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");

Figure contains an axes object. The axes object with title Undistorted Images contains an object of type image.

영상 간의 대응점 찾기

추적하기 좋은 특징을 검출합니다. '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));

Figure contains an axes object. The axes object with title 150 Strongest Corners from the First Image contains 2 objects of type image, line. One or more of the lines displays its values using only markers

% 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");

Figure contains an axes object. The axes object with title Tracked Features contains 4 objects of type image, line. One or more of the lines displays its values using only markers

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");

Figure contains an axes object. The axes object with title Epipolar Inliers contains 4 objects of type image, line. One or more of the lines displays its values using only markers

카메라 자세 계산하기

첫 번째 카메라를 기준으로 두 번째 카메라의 위치와 방향을 계산합니다. 참고로, 평행 이동은 스케일로만(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");

Figure contains an axes object. The axes object with title Up to Scale Reconstruction of the Scene, xlabel x-axis, ylabel y-axis contains 21 objects of type line, text, patch, scatter.

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

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

Figure contains an axes object. The axes object with title Estimated Location and Size of the Globe, xlabel x-axis, ylabel y-axis contains 22 objects of type line, text, patch, scatter, surface.

장면의 메트릭 복원

지구본의 실제 반지름은 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");

Figure contains an axes object. The axes object with title Metric Reconstruction of the Scene, xlabel x-axis (cm), ylabel y-axis (cm) contains 21 objects of type line, text, patch, scatter.

요약

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

참고 문헌

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