에피폴라 기하 — 두 장의 사진이 서로에게 거는 단 하나의 제약
서론
지난 3D Gaussian Splatting 글에서 슬쩍 넘어간 문장이 하나 있습니다.
“포즈는 보통 COLMAP 같은 SfM으로 먼저 구합니다.”
그 “먼저”에 해당하는 분야가 다중 시점 기하(Multiple View Geometry) 입니다. Hartley & Zisserman의 같은 이름의 교과서가 사실상 표준 레퍼런스이고,1 그 전체 논의의 출발점이 오늘 다룰 에피폴라 기하(epipolar geometry) 입니다.
한 문장으로 줄이면 이렇습니다.
같은 3차원 점을 두 위치에서 찍었다면, 두 이미지 위의 좌표는 아무 값이나 될 수 없다. 카메라의 내부 파라미터도, 장면의 생김새도 몰라도, 두 좌표를 묶는 스칼라 방정식 하나가 반드시 성립한다.
이 방정식 하나에서 스테레오 매칭, 비주얼 오도메트리, SfM, 그리고 SLAM 초기화가 전부 흘러나옵니다.
동차 좌표, 랭크, 자유도, SVD 같은 도구가 낯설다면 다중 시점 기하를 읽기 위한 최소한의 수학을 먼저 읽고 오셔도 좋습니다.
1. 사진 한 장은 깊이를 버린다
핀홀 카메라는 3차원 점 $X$를 이미지 점 $x$로 보냅니다. 동차 좌표로 쓰면 곱셈 한 번입니다.
\[x \simeq P X, \qquad P = K\,[\,R \mid t\,]\]$K$는 초점거리·주점 같은 내부 파라미터, $R$과 $t$는 카메라가 세상 어디에 어떤 자세로 있는지를 나타내는 외부 파라미터입니다. $\simeq$ 는 “스케일 차이를 무시하고 같다”는 뜻인데, 바로 이 스케일이 문제의 핵심입니다.
이미지 점 $x$를 하나 찍었다고 해 봅시다. 이 점을 만든 3차원 점은 어디에 있을까요? 알 수 없습니다. 카메라 중심에서 $x$ 방향으로 뻗은 광선 위 어디든 될 수 있습니다. 가까이 있는 작은 물체와 멀리 있는 큰 물체는 같은 픽셀을 만듭니다.
\[\text{이미지의 점 하나} \;\longleftrightarrow\; \text{3차원 공간의 광선 하나}\]깊이가 사라졌습니다. 한 장으로는 여기까지입니다.
2. 두 번째 카메라가 들어오는 순간
이제 다른 위치에서 한 장 더 찍습니다. 그러면 갑자기 강력한 제약이 생깁니다.
첫 번째 이미지의 점 $x$에 대응하는 3차원 점은 광선 위 어딘가에 있습니다. 그 광선 전체를 두 번째 카메라로 찍으면 무엇이 될까요? 직선입니다. 즉 $x$의 짝은 두 번째 이미지 어디에나 있을 수 있는 게 아니라, 딱 하나의 직선 위에 있어야 합니다.
두 카메라 중심 $C$, $C’$ 와 3차원 점 $X$ 가 만드는 평면이 에피폴라 평면 $\pi$ 다. 이 평면이 각 이미지와 만나 생기는 직선이 에피폴라 선 $l$, $l’$ 이고, 베이스라인이 이미지를 뚫는 점이 에피폴 $e$, $e’$ 다.
용어를 정리하면 이렇습니다.
| 이름 | 정의 |
|---|---|
| 베이스라인 | 두 카메라 중심 $C$, $C’$ 를 잇는 선분 |
| 에피폴라 평면 $\pi$ | $C$, $C’$, $X$ 세 점이 만드는 평면 |
| 에피폴 $e$, $e’$ | 베이스라인이 각 이미지 평면을 뚫는 점 (= 다른 카메라 중심의 상) |
| 에피폴라 선 $l$, $l’$ | 에피폴라 평면과 각 이미지 평면이 만나 생기는 직선 |
여기서 두 가지 사실이 따라 나옵니다.
① 모든 에피폴라 선은 에피폴을 지난다. 3차원 점 $X$를 어디로 옮기든 에피폴라 평면은 항상 베이스라인을 품고, 따라서 그 평면이 만든 직선은 항상 에피폴을 지납니다. 에피폴라 선들은 에피폴을 중심으로 하는 직선다발(pencil) 을 이룹니다.
② 대응점 탐색이 2차원에서 1차원으로 줄어든다. 이것이 실용적으로 가장 중요한 결론입니다. 640×480 이미지에서 짝을 찾을 때 30만 픽셀을 다 뒤질 필요 없이, 직선 하나 위의 수백 픽셀만 보면 됩니다. 스테레오 매칭과 특징점 추적이 현실적인 비용으로 돌아가는 이유입니다.
3. Fundamental matrix — 제약을 행렬 하나로
“점 $x$가 주어지면 대응점은 직선 $l’$ 위에 있다”는 관계를, 놀랍게도 $3 \times 3$ 행렬 하나의 곱으로 쓸 수 있습니다.
\[l' = F x\]이 $F$가 기본 행렬(fundamental matrix) 입니다. 그리고 대응점 $x’$이 그 직선 위에 있다는 조건($l’^\top x’ = 0$)이 바로 에피폴라 기하의 핵심 방정식입니다.
\[x'^\top F\, x = 0\]좌표 두 개를 넣으면 스칼라 하나가 나오고, 그 값이 0이어야 한다 — 이게 전부입니다. 성질을 정리하면:
- 랭크 2, 따라서 $\det F = 0$. 모든 에피폴라 선이 에피폴 한 점에서 만나기 때문에 생기는 퇴화입니다.
- 자유도 7. 원소는 9개지만 전체 스케일이 무의미하고($-1$), 랭크 제약이 하나 더 걸립니다($-1$).
- 에피폴은 영벡터. $F e = 0$, $F^\top e’ = 0$. 그래서 $F$만 알면 SVD로 에피폴이 바로 나옵니다.
- 방향을 뒤집으면 전치. 두 번째 이미지에서 첫 번째로 가는 관계는 $F^\top$ 입니다.
- 캘리브레이션이 필요 없다. $K$를 몰라도, 초점거리를 몰라도, 픽셀 좌표 대응만으로 구할 수 있습니다.
마지막 항목이 $F$의 진짜 가치입니다. 카메라에 대해 아무것도 모르는 사진 두 장에서도 기하 관계를 뽑아낼 수 있다는 뜻이니까요.
4. Essential matrix — 캘리브레이션을 알 때
카메라 내부 파라미터 $K$를 알고 있다면 한 걸음 더 갈 수 있습니다. 픽셀 좌표를 $K^{-1}$로 되돌려 정규화 좌표로 바꾸면, 같은 제약이 더 단순한 행렬로 표현됩니다. 이것이 필수 행렬(essential matrix) $E$ 입니다.
\[\hat{x}'^\top E\, \hat{x} = 0, \qquad \hat{x} = K^{-1} x\] \[E = [t]_\times R, \qquad F = K'^{-\top} E\, K^{-1}\]여기서 $R$과 $t$는 두 카메라 사이의 상대 회전과 이동이고, $[t]$에 붙은 기호는 벡터를 외적 연산에 대응하는 반대칭 행렬로 바꾼 것입니다. 즉 $E$는 카메라 사이의 상대 자세 그 자체를 담고 있습니다.
- 자유도 5. 회전 3 + 이동 3 − 스케일 1.
- 특이값이 $(\sigma, \sigma, 0)$. 두 개가 같고 하나가 0인 아주 특별한 구조입니다.
- 분해하면 자세가 나온다. $E$를 SVD로 분해하면 $(R, t)$ 후보가 네 쌍 나옵니다. 이 중 “복원된 3차원 점이 두 카메라 모두의 앞쪽에 있어야 한다”는 조건(cheirality)을 만족하는 하나만 물리적으로 가능합니다.
그리고 여기서 단안 비전의 근본적인 한계가 드러납니다. 자유도 6이어야 할 상대 자세가 5밖에 안 되는 이유는 $t$의 크기를 알 수 없기 때문입니다.
장면 전체가 2배 커지고 카메라 이동도 2배 커지면, 찍히는 사진은 완전히 똑같습니다.
이것이 스케일 모호성(scale ambiguity) 입니다. 단안 카메라만으로는 “저 문이 2미터 앞에 있다”를 절대 알 수 없고, 오직 “이동량의 몇 배 거리에 있다”까지만 알 수 있습니다. 단안 SLAM이 IMU·휠 오도메트리·스테레오·기지 크기 물체 중 뭐라도 하나를 필요로 하는 이유가 바로 이것입니다.
같은 주제를 한국어로 다룬 글로 다크프로그래머의 영상 Geometry #7 — Epipolar Geometry가 있습니다. 이 글보다 짧고, 그림과 함께 개념을 잡기에 좋습니다.
5. 데이터에서 $F$ 구하기 — 정규화 8점 알고리즘
$x’^\top F x = 0$ 은 $F$의 원소 9개에 대해 선형입니다. 대응점 한 쌍마다 방정식 하나. 스케일이 무의미하므로 8쌍이면 풀립니다. 이것이 고전적인 8점 알고리즘입니다.2
대응 하나를 풀어쓰면 계수 벡터가 이렇게 나옵니다.
\[\begin{bmatrix} u'u & u'v & u' & v'u & v'v & v' & u & v & 1 \end{bmatrix} \operatorname{vec}(F) = 0\]이런 행을 쌓아 $A f = 0$ 을 만들고, $A$의 최소 특이벡터를 취하면 끝 — 인 것 같지만, 여기에 두 가지 함정이 있습니다.
함정 1: 랭크 제약. 그냥 푼 $F$는 랭크 3이 되어 버립니다. 그러면 에피폴라 선들이 한 점에서 만나지 않습니다. SVD로 가장 작은 특이값을 0으로 강제해 랭크 2로 되돌려야 합니다.
함정 2: 좌표 스케일. 픽셀 좌표는 수백 단위인데, $A$의 열에는 $u’u$ 같은 항($\sim 10^5$)과 상수 1이 섞여 있습니다. 열 사이 크기 차이가 $10^5$ 배면 조건수가 폭발해서, 수치적으로 엉망인 답이 나옵니다. 각 이미지의 점들을 평균 0, 원점까지의 평균 거리 $\sqrt{2}$ 가 되도록 미리 정규화하면 이 문제가 사라집니다. Hartley가 지적한 이 한 단계가 결과를 극적으로 바꾸기 때문에, 오늘날 8점 알고리즘은 사실상 정규화 8점 알고리즘을 뜻합니다.3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import numpy as np
def normalize_pts(pts):
"""평균 0, 원점까지 평균거리 √2 가 되도록 하는 상사변환 T 와 정규화된 점들."""
c = pts.mean(axis=0)
s = np.sqrt(2) / np.linalg.norm(pts - c, axis=1).mean()
T = np.array([[s, 0, -s * c[0]], [0, s, -s * c[1]], [0, 0, 1.0]])
q = (T @ np.hstack([pts, np.ones((len(pts), 1))]).T).T
return q[:, :2], T
def eight_point(x1, x2):
"""정규화 8점 알고리즘. x2^T F x1 = 0 을 만족하는 F를 돌려준다."""
p1, T1 = normalize_pts(x1)
p2, T2 = normalize_pts(x2)
u1, v1 = p1[:, 0], p1[:, 1]
u2, v2 = p2[:, 0], p2[:, 1]
A = np.stack([u2 * u1, u2 * v1, u2, v2 * u1, v2 * v1, v2,
u1, v1, np.ones_like(u1)], axis=1)
F = np.linalg.svd(A)[2][-1].reshape(3, 3) # A f = 0 의 최소제곱 해
U, S, Vt = np.linalg.svd(F)
F = U @ np.diag([S[0], S[1], 0.0]) @ Vt # 함정 1: 랭크 2 강제
F = T2.T @ F @ T1 # 정규화를 원래 좌표계로 되돌린다
return F / F[2, 2]
합성 장면으로 확인해 봅니다. 3차원 점 14개를 두 카메라로 투영하고, 검출 오차를 흉내 내 0.5픽셀 가우시안 노이즈를 섞은 뒤 위 함수로 $F$를 추정했습니다.
노이즈 섞인 대응점만으로 추정한 $F$ 로 그린 에피폴라 선. 왼쪽 점의 짝은 오른쪽의 같은 색 직선 위에 정확히 놓인다. 카메라가 앞으로 전진했기 때문에 모든 직선이 한 점(에피폴)에서 만난다 — 이 경우의 에피폴을 확장 초점(FOE)이라고 부른다.
수치로도 확인됩니다. 대응점이 자기 에피폴라 선에서 벗어난 평균 거리는 0.669픽셀로 주입한 노이즈 수준과 같고, 정답 $F$와의 상대 오차는 2.6 % 였습니다. 여덟 개 남짓한 점 대응만으로 두 시점 사이의 기하가 복원된 셈입니다.
실전에서는 두 가지가 더 붙습니다.
- RANSAC. 특징점 매칭에는 항상 잘못된 짝이 섞입니다. 8쌍을 무작위로 뽑아 $F$를 만들고 나머지가 얼마나 동의하는지 세는 과정을 반복해 아웃라이어를 걸러 냅니다. 실제로 에피폴라 제약은 매칭 검증 도구로 가장 많이 쓰입니다. RANSAC 자체의 원리와 파라미터 감각은 다크프로그래머의 RANSAC의 이해와 영상처리 활용이 잘 정리해 두었습니다.
- 5점 알고리즘. 캘리브레이션을 안다면 $E$의 자유도가 5뿐이므로 대응 5쌍으로 풉니다.4 RANSAC은 표본이 적을수록 반복 횟수가 확 줄기 때문에, 캘리브레이션된 시스템에서는 5점법이 표준입니다.
6. 로보틱스에서 어디에 쓰이나
- 스테레오 정류(rectification). 두 이미지를 변형해 에피폴을 무한대로 보내면 에피폴라 선이 전부 수평선이 됩니다. 그러면 대응점 탐색이 “같은 행을 훑기”로 단순해져서, 실시간 밀집 깊이 맵이 가능해집니다.
- 비주얼 오도메트리 / SLAM 초기화. 연속된 두 프레임에서 $E$를 구해 분해하면 상대 자세 $(R, t)$가 나옵니다. 단안 SLAM이 맨 처음 지도를 만들 때 하는 일이 정확히 이것입니다.
- SfM. 두 시점 관계를 여러 이미지로 확장해 전역 최적화(번들 조정)로 넘기면 COLMAP이 됩니다. 그 결과가 지난 글에서 3DGS의 입력으로 쓴 카메라 포즈와 희소 점군입니다.
- 매칭 검증. 위에서 말한 RANSAC 아웃라이어 제거. 루프 클로저 후보를 검증할 때도 같은 방식을 씁니다.
7. 조심할 것 — 퇴화 상황
에피폴라 기하는 두 조건이 깨지면 그대로 무너집니다.
- 순수 회전. 카메라가 제자리에서 돌기만 하면 베이스라인이 0이라 에피폴라 평면 자체가 정의되지 않습니다. $F$를 구하려 해도 의미 있는 답이 없습니다. 이때 두 이미지를 묶는 올바른 모델은 호모그래피 $H$ 입니다.
- 평면 장면. 모든 3차원 점이 한 평면 위에 있으면 8점 알고리즘의 방정식들이 퇴화해 해가 무한히 많아집니다. 벽을 정면으로 찍거나 바닥만 보는 상황이 정확히 여기에 해당합니다. 그래서 실전 시스템은 $F$와 $H$를 둘 다 추정해 어느 쪽이 데이터를 더 잘 설명하는지 비교한 뒤 모델을 고릅니다.
- 짧은 베이스라인. 이동이 작으면 삼각측량의 조건수가 나빠져 깊이 추정이 크게 흔들립니다.
- 스케일 모호성. 앞서 말한 대로, 단안만으로는 절대 크기를 얻을 수 없습니다.
마치며
세 가지만 남기면 충분합니다.
- 기하 — 두 카메라 중심과 3차원 점이 만드는 평면이 각 이미지를 자르면서, 대응점 탐색을 2차원에서 1차원으로 줄인다.
- 대수 — 그 제약 전체가 $x’^\top F x = 0$ 이라는 방정식 하나로 압축된다. 캘리브레이션을 알면 $F$는 상대 자세를 그대로 담은 $E$ 로 바뀌고, 이를 분해하면 $R$ 과 $t$ 가 나온다.
- 추정 — 정규화 8점 알고리즘 + 랭크 2 강제 + RANSAC. 정규화를 빼먹으면 답이 망가진다.
다음 글에서는 여기서 얻은 $(R, t)$를 가지고 실제로 3차원 점을 복원하는 삼각측량과, 그 오차를 전부 함께 줄이는 번들 조정으로 넘어가 보겠습니다. 그 길 끝에 SfM 파이프라인과 SLAM이 있습니다.
참고문헌
Hartley, R., & Zisserman, A. (2004). Multiple View Geometry in Computer Vision (2nd ed.). Cambridge University Press. — 에피폴라 기하는 9장, $F$ 추정은 11장. ↩︎
Longuet-Higgins, H. C. (1981). A computer algorithm for reconstructing a scene from two projections. Nature, 293, 133–135. ↩︎
Hartley, R. (1997). In Defense of the Eight-Point Algorithm. IEEE TPAMI, 19(6), 580–593. ↩︎
Nistér, D. (2004). An Efficient Solution to the Five-Point Relative Pose Problem. IEEE TPAMI, 26(6), 756–770. ↩︎