Contents — find the section you need

카메라로 촬영한 이미지에 지도상의 여러 알려진 3D 점이 있고, 이 점들이 이미지의 점들에 대응한다고 가정해 봅시다. 카메라의 위치와 방향을 찾는 문제가 PnP(Perspective-n-Point) 문제입니다. 이 문제는 비주얼 SLAM 지도 추적, AR에서의 가상 객체 오버레이, 로봇의 손-눈 캘리브레이션, 측량 카메라의 자세 추정 등 다양한 분야에서 공통적으로 사용됩니다.

0.30초 요약

  • 입력은 카메라 내부 행렬 K, 알려진 3D 점 \mathbf X_i, 그리고 이에 대응하는 이미지 점 \mathbf u_i입니다. 출력은 회전 R과 이동 t입니다.

  • 이 문제는 투영 방정식 \mathbf u_i\sim K(R\mathbf X_i+t)의 재투영 오차를 최소화합니다. 3개의 점을 사용할 경우, P3P는 여러 후보 해를 제공합니다. 4개 이상의 가상 제어점을 사용할 경우, 중복성을 통해 이상치를 감지할 수 있습니다.

  • EPnP는 각 점을 4개의 가상 제어점의 선형 조합으로 표현하여 많은 점에 대한 계산을 빠르게 수행합니다. 이후 Levenberg-Marquardt와 같은 비선형 최적화를 통해 결과를 더욱 정밀하게 다듬습니다.
  • 이상치가 대응점에 섞이면 전체 포즈 추정치가 무너질 수 있으므로, RANSAC-PnP, 깊이 확인, 프레임 간 일관성 검사를 통해 검증합니다.
  • 점들이 거의 동일 평면상에 있거나, 시차가 작거나, 내부 매개변수가 잘못되었거나, 롤링 셔터 현상이나 장면에 동적 객체가 있는 경우 퇴화 및 발산이 흔히 발생합니다.

1. 투영 모델

Diagram 1 · Use the button to switch views
PnP flow that projection known 3D points with a pose hypothesis and uses residuals to update the pose through RANSAC and nonlinear refinement

그림 1 — 매칭 ID는 3D-2D 대응 관계를 정의합니다. PnP는 포즈 가설을 형성하고, 재투영 잔차를 통해 이상치를 제거하며, 월드 좌표를 카메라 좌표로 매핑하는 R,t을 정제합니다.

카메라 좌표계의 한 점을 \mathbf X_c=R\mathbf X_w+t이라고 합시다. 핀홀 모델에서 정규화된 이미지 좌표는 다음과 같습니다.

x=\frac{X_c}{Z_c},\qquad y=\frac{Y_c}{Z_c}

그리고 픽셀 좌표는 내부 행렬을 통해 얻어집니다.

K=\begin{bmatrix}f_x&0&c_x\\0&f_y&c_y\\0&0&1\end{bmatrix}

as \mathbf u\sim K\mathbf X_c, R\in SO(3)는 회전이고 t은 병진입니다. 렌즈 왜곡이 있는 경우 투영 전후에 왜곡 보정이 필요합니다.

미지수는 회전 3개와 병진 3개를 포함한 총 6개의 자유도입니다. 3D 점 \mathbf X_i와 관측값 \mathbf u_i 사이의 n 대응 관계가 주어졌을 때, 재투영 오차

E(R,t)=\sum_{i=1}^{n}\rho\left(\left\|\mathbf u_i-\pi(K(R\mathbf X_i+t))\right\|^2\right)

를 최소화합니다. \pi은 원근 분할이고 \rho는 Huber의 손실 함수와 같은 강건한 손실 함수입니다.

변환과 카메라 위치를 혼동하지 마세요

OpenCV의 solvePnP은 객체/월드 좌표를 카메라 좌표계로 매핑하는 변환에 대해 rvec, tvec을 반환합니다. 카메라 중심을 얻으려면 세계 좌표를 얻으려면 \mathbf C_w=-R^Tt를 사용하고, 카메라 포즈를 얻으려면 T_{cw}을 반전시켜 T_{wc}를 얻습니다. tvec 자체를 카메라의 세계 위치로 취급하는 것은 흔한 오류입니다. 또한 입력 이미지의 점들이 이미 왜곡 보정된 경우 왜곡을 두 번 적용하지 않도록 주의해야 합니다.

2. P3P, AP3P 및 EPnP

P3P(Perspective-3-Point)는 세 점의 이미지 각도와 3D 점들 사이의 거리를 이용하여 카메라 중심에서 거리를 계산하며, 최대 4개의 해를 가집니다. 올바른 해는 네 번째 점 또는 지도의 알려진 포즈와 비교하여 선택됩니다. AP3P는 해를 대수적으로 재구성하는 빠른 변형입니다.

점이 많은 경우, EPnP(Efficient PnP)는 각 3D 점을 4개의 가상 제어점의 가중 합으로 표현합니다.

\mathbf X_i=\sum_{j=1}^{4}\alpha_{ij}\mathbf C_j,\qquad \sum_j\alpha_{ij}=1

제어점의 카메라 좌표는 선형 방정식을 통해 구하고, 회전과 변환은 이를 통해 복원합니다. 계산 비용이 점의 개수에 거의 선형적으로 비례하기 때문에 SLAM의 수많은 랜드마크를 이용하여 초기 포즈를 구축하는 데 매우 적합합니다. 초기 해를 구한 후에는 Levenberg-Marquardt 알고리즘을 사용하여 재투영 오차를 반복적으로 보정합니다.

3. RANSAC-PnP

특징점 대응은 유사한 패턴, 움직이는 객체, 잘못된 맵 ID와 혼합되어 나타납니다. 표준적인 접근 방식은 RANSAC입니다. 최소한의 점 집합으로 임시 포즈를 구축하고, 모든 대응점을 재투영한 다음, 임계값 내에 포함되는 인라이어의 개수를 계산합니다. 이상치 비율 \epsilon, 최소 샘플 크기 s, 성공 확률 p을 고려했을 때 필요한 반복 횟수 N은 다음과 같이 결정됩니다.

N\ge\frac{\log(1-p)}{\log(1-(1-\epsilon)^s)}

이상치 비율이 높아질수록 필요한 반복 횟수가 급격히 증가하므로, 특징점 비율 테스트, 그리드 기반 분산 또는 동적 객체 마스크를 사용하여 \epsilon 값을 미리 낮추십시오. OpenCV의 solvePnPRansac은 점 개수, 플래그(EPNP, P3P, SQPNP 등), 재투영 임계값 및 신뢰도를 명시적으로 지정하여 사용합니다.

4. 퇴화 현상 파악

동일 평면상의 점 집합

모든 3D 점이 동일 평면상에 있는 경우, PnP에서 얻은 깊이와 포즈 정보가 모호해지며, 기하학적 구조는 호모그래피로도 설명할 수 있습니다. 체커보드 캘리브레이션은 의도적으로 평면을 사용하지만, 포즈의 자유도를 충분히 제한할 수 있는 시점과 점 배치를 선택해야 합니다. 정면에서 본 단일 AR 마커의 깊이 정보가 불안정해지는 현상도 이와 유사합니다.

좁은 이미지 범위 및 긴 범위

PnP는 하나의 이미지와 알려진 3D 포인트를 직접 사용하므로 프레임 간 시차 자체는 필수 입력값이 아닙니다. 그러나 대응점이 이미지의 작은 영역에만 존재하거나, 목표물이 멀리 떨어져 작게 보이거나, 3D 포인트의 깊이 변화가 적을 경우 PnP는 제대로 작동하지 않습니다. 인라이어 개수만으로 결과를 받아들이지 말고, 이미지 범위, 재투영 RMSE, 포즈 공분산 또는 교란에 대한 민감도를 검사한 후 필요에 따라 IMU 또는 깊이 센서를 융합해야 합니다.

보정 및 타이밍

초점 거리, 주점, 왜곡의 오차는 모든 포인트에 걸쳐 체계적인 재투영 오차로 이어집니다. 줌, 온도 또는 초점에 따라 내부 행렬이 변하는 렌즈는 재보정이 필요합니다. 차량이나 드론에서 롤링 셔터의 행 타이밍이 IMU와 동기화되지 않으면 PnP는 "휘어진" 카메라 포즈를 반환합니다.

5. 비주얼 SLAM에서 PnP의 역할

비주얼 SLAM에서, 대응점의 수가 많을수록 이전에 삼각화된 지도 포인트가 증가함에 따라 PnP를 사용하여 카메라 포즈를 프레임 단위로 추적할 수 있습니다. 포즈를 고정한 상태에서 새로운 포인트를 삼각화하고, 충분한 키프레임이 누적되면 Bundle Adjustment가 포즈와 지도를 동시에 최적화합니다. 이를 쉽게 이해하려면 분업으로 생각하면 됩니다. PnP는 경량 프런트엔드이고, Bundle Adjustment는 전역적인 일관성을 처리합니다.

6. 구현 체크리스트

  1. 체커보드 또는 유사한 방법을 사용하여 K와 왜곡을 보정하고 재투영 오차를 기록합니다.

  2. 3D 포인트의 단위(m/mm)와 좌표계를 이미지 포인트의 왜곡 보정 상태에 맞춰 정렬합니다.

  3. 비율 테스트, 상호 최근접 이웃, 시간 추적을 사용하여 대응점을 좁힙니다.

  4. RANSAC-PnP를 사용하여 이상치를 제거하고, 정상점 분포와 재투영 오차를 저장합니다.

  5. 깊이가 양수인지, 포즈 변화가 물리적으로 타당한지 확인합니다. 이전 프레임과의 차이가 합리적인지 확인합니다.

  6. 조건이 좋지 않으면 IMU, 깊이, 호모그래피 또는 재초기화를 사용합니다.

최소 구현 순서

OpenCV를 사용하는 경우, 먼저 solvePnPRansac에서 rvec, tvec, inliers를 얻고, 인라이어만 solvePnPRefineLM에 전달한 다음, projectPoints을 사용하여 인라이어 RMSE와 오차 분포를 직접 계산합니다. API 반환만으로는 과도한 불일치, 밀집된 점 또는 물리적으로 불가능한 포즈를 감지할 수 없습니다.

ok, rvec, tvec, inliers = cv2.solvePnPRansac(
    object_points, image_points, K, dist,
    flags=cv2.SOLVEPNP_EPNP,
    reprojectionError=3.0, confidence=0.999, iterationsCount=200,
)
if not ok or inliers is None or len(inliers) < 6:
    raise RuntimeError("PnP failed or has too few inliers")

idx = inliers.ravel()
rvec, tvec = cv2.solvePnPRefineLM(
    object_points[idx], image_points[idx], K, dist, rvec, tvec
)
projected, _ = cv2.projectPoints(object_points[idx], rvec, tvec, K, dist)
rmse = np.sqrt(np.mean(np.sum(
    (projected.reshape(-1, 2) - image_points[idx].reshape(-1, 2)) ** 2,
    axis=1,
)))
R, _ = cv2.Rodrigues(rvec)
camera_center_world = -R.T @ tvec.reshape(3, 1)

3.0 px이나 6개의 인라이어는 보편적인 허용 임계값이 아닙니다. 이는 이 예제의 시작 값일 뿐입니다. 이미지 해상도, 특징점 정밀도 및 애플리케이션에서 허용하는 포즈 오차를 기반으로 임계값을 도출하십시오. 또한 인라이어가 이미지의 한쪽 모서리에 밀집되어 있지 않고 모든 점에 카메라 프레임 깊이가 있는지 확인하십시오. Z_c>0

7. 요약

PnP는 3D 지도와 2D 이미지 간의 대응점을 6자유도 카메라 포즈로 변환하는 브리지입니다. P3P/EPnP를 사용하여 초기 솔루션을 구축하고, RANSAC으로 이상치를 제거한 다음, 비선형 최적화로 개선합니다. 점 배치, 캘리브레이션, 시차 및 시간 동기화를 함께 관리해야만 Visual SLAM 또는 AR에 안정적인 포즈 추정값을 얻을 수 있습니다.

이해도 확인
PnP는 두 개의 이미지만 필요한가요?

기본 입력은 알려진 3D 점, 해당 2D 이미지 대응점, 그리고 카메라 내부 정보입니다. 이는 2D 대 2D 매칭을 통한 모션 추정과는 다릅니다.

참고 자료

What to read next

Review the background에피폴라 기하학 — 두 이미지에서 깊이와 카메라 움직임 읽기Continue the series모션 복원 기초 - 정렬되지 않은 사진 세트에서 3D 이미지와 카메라 위치를 함께 복원하는 방법Explore another aspect of this field이미지 밝기·휘도 Lab — 노출, 감마, 클리핑 비교