두 시점에서의 움직임 기반 구조
움직임 기반 구조(SfM)는 2차원 영상 세트로부터 장면의 3차원 구조를 추정하는 과정입니다. 이 예제에서는 두 영상으로부터 보정된 카메라의 자세를 추정하고, 미지의 스케일링 인자로 장면의 3차원 구조를 복원한 후, 크기를 알고 있는 객체를 검출하여 실제 스케일링 인자를 복원하는 방법을 보여줍니다.
개요
이 예제에서는 카메라 보정기 앱을 사용하여 보정된 카메라로 촬영한 한 쌍의 2차원 영상에서 3차원 장면을 복원하는 방법을 보여줍니다. 이 알고리즘은 다음 단계로 구성됩니다.
두 영상 간에 희소한 점 집합을 매칭시킵니다. 두 영상 간의 대응점은 여러 방법으로 찾을 수 있습니다. 이 예제에서는
detectMinEigenFeatures
함수를 사용하여 첫 번째 영상에서 코너를 검출하고vision.PointTracker
를 사용하여 두 번째 영상에서 해당 코너를 추적합니다. 또는extractFeatures
를 사용한 후matchFeatures
를 사용할 수도 있습니다.estimateEssentialMatrix
를 사용하여 Fundamental 행렬을 추정합니다.estrelpose
함수를 사용하여 카메라의 움직임을 계산합니다.두 영상 사이에서 조밀한 점 집합을 매칭시킵니다.
'MinQuality'
의 값을 줄인 상태로detectMinEigenFeatures
를 사용하여 해당 점을 다시 검출해서 더 많은 점을 구합니다. 그런 다음vision.PointTracker
를 사용하여 두 번째 영상에서 이 조밀한 점들을 추적합니다.triangulate
를 사용하여 매칭되는 점들의 3차원 위치를 확인합니다.크기를 알고 있는 객체를 검출합니다. 이 장면에는 반지름이 10cm인 지구본이 있습니다.
pcfitsphere
를 사용하여 포인트 클라우드에서 지구본을 찾습니다.실제 스케일을 복구하여 메트릭을 복원합니다.
한 쌍의 영상 읽어 들이기
한 쌍의 영상을 작업 공간으로 불러옵니다.
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.