Calibrating Lens Distortion with Realtime Processing
Abstract
사진을 촬영하다 보면 풍경이나 피사체가 왜곡되어 촬영되는 현상을 자주 느낄 수 있다. 화면 가장자리에 가까울 수록, 카메라로부터 멀리 위치한 피사체일수록 뒤틀리거나 기울어져 보이는 왜곡 현상이다.
왜곡 현상은 특히 로우 앵글(아래에서 위)로 촬영할 때 특히 심한 것을 느낄 수 있는데, 예를 들어 스마트폰 전면 카메라로 본인의 얼굴을 찍을 때, 밑에서 찍으면 상당히 웃기게 나오는 것을 확인할 수 있다. 그리고 정면에서 촬영한다고 하더라도, 얼굴을 화면 상단에 위치시킨 사진과 하단에 위치시킨 사진을 비교해보면 인상이 확연히 달라진다. 보통 얼굴이 상단에 있으면 더 예쁘게 나온다.
렌즈 왜곡 보정은 영상 처리 분야에서 거의 필수적인 내용이지만, 사람들이 일상적으로 사용하는 일반 휴대폰 카메라 앱 등에서는 관련 기능을 찾아볼 수 없었다.
예전에 친구의 사진을 우스꽝스럽게 왜곡시키며 놀았던 기억이 있는데, 반대로 이미지 왜곡을 사용하여 인물 사진을 좀 더 잘 나오게 할 수 있을지 궁금해져서 관심을 갖게 되었다.
인물 사진을 많이 찍어본 사람이 아니라면 찍기가 상당히 어렵다. 얼굴을 가장자리에 배치하면 찌그러지고, 그렇다고 가운데 위치하면 방사왜곡 현상으로 얼굴이 호빵맨이 될 수도 있다. 그래서 인물 사진은 가까이서 찍는 것보다 멀리 떨어져서 찍거나 피사체를 확대해서 촬영하는게 훨씬 잘 나온다. 문제는 전면 카메라로 촬영하는 것인데, 카메라와 얼굴 간의 거리가 애매해서 연예인급 외모를 보유하지 않는 이상 마음에 드는 사진을 건지려면 여러 번 촬영해야 한다.
렌즈 왜곡의 종류
렌즈 왜곡에는 크게 방사왜곡(radial distortion)과 접선왜곡(tangential distortion)이 있다.
방사왜곡(radial distortion)은 볼록렌즈의 굴절률에 영향을 받아 영상의 왜곡 정도가 중심에서의 거리에 의해 결정된다. 예를 들어 현관문에 달린 작은 도어렌즈나 흔히 사진 편집 프로그램에서 볼 수 있는 물고기눈 효과(fish-eye effect)라고 불리는 필터 등이 방사 왜곡에 해당한다고 할 수 있다.
반면 접선왜곡(tangential distortion)은 카메라 제조 과정에서 렌즈와 이미지 센서(CMOS 등)의 수평이 맞지 않거나 렌즈 자체의 중앙 정렬이 맞지 않아서 발생하는 왜곡으로, 원형보다는 타원형 형태로 분포가 달라진다. 따라서 접선왜곡은 decentering distortion이라고도 불린다.
다만 현재의 발전된 기술의 수준을 보면 저가형 카메라가 아닌 이상 생산 과정에서 접선왜곡이 육안으로 인식될 정도로 오차를 발생 시키지는 않을 듯하다.
수학적 관점
만약 렌즈에 왜곡이 없다고 할 경우, 3차원 공간 상의 한 점(
위 공식에서 3d 점을
그러나 실제
여기서
= normalized undistorted 좌표 ( ) = 왜곡된 normalized 좌표 = 방사왜곡(Radial distortion) 계수 (보통 까지 사용) = 접선왜곡(Tangential distortion) 계수
따라서 Brown-Conrady 모델은 Radial 부분과 Tangential 부분으로 구성되어 있어 방사왜곡과 접선왜곡을 함께 처리가 가능하다. 이 모델은 현재 컴퓨터 비전에서 가장 표준으로 사용되는 렌즈 왜곡 모델이며, OpenCV, MATLAB 등 거의 모든 컴퓨터 비전 라이브러리에서 이 모델이 포함되어 있다.
중요한 것은 Brown-Conrady 모델은 렌즈 왜곡 모델이지, 렌즈 왜곡 보정 모델은 아니라는 사실이다. 왜곡된 좌표 => undistorted 좌표로 가는 공식이 역함수로 쉽게 풀리지 않기 때문에, 실제로 역방향 매핑(Backward Mapping) + 반복(iterative) 방법을 사용한다. 즉 역함수가 없으므로 반복적으로 계산을 수행하여 근사치를 구하는 수밖에 없다. 또한 렌즈 왜곡은 광학적으로 매우 복잡한 현상이고, Brown-Conrady 모델은 이를 근사한 모델일 뿐이다.
왜곡 보정(Undistort) 방법
왜곡을 보정하기 위해서는 크게 다음 단계를 따른다.
- 먼저 출력 이미지의 모든 픽셀(undistorted coordinates)을 정한다.
- 각 픽셀이 원래 왜곡된 이미지의 어느 위치에서 왔는지 찾는다.
- 그 위치의 색상을 가져와 출력 이미지에 채운다.
이 과정을 역매핑(Reverse Mapping) 이라고 부른다.
하나의 픽셀에 대해 undistorted normalized 좌표를 구하는 간단한 반복 알고리즘은 다음과 같다. 보통 Newton-Raphson 반복법이나 고정점 반복을 사용하는데, 실제로는 고정점 반복이 자주 쓰이는 형태이므로 아래 코드에서는 고정점 반복법(Fixed-Point Iteration)을 사용한다.
def undistort_point(xd, yd, k1, k2, k3, p1, p2, max_iter=20):
x = xd # 초기값
y = yd
for _ in range(max_iter):
r2 = x*x + y*y
r4 = r2*r2
r6 = r4*r2
# Radial distortion factor
radial = 1 + k1*r2 + k2*r4 + k3*r6
# Tangential distortion
dx = 2*p1*x*y + p2*(r2 + 2*x*x)
dy = p1*(r2 + 2*y*y) + 2*p2*x*y
# Update
x_new = (xd - dx) / radial
y_new = (yd - dy) / radial
if abs(x_new - x) < 1e-8 and abs(y_new - y) < 1e-8:
break
x, y = x_new, y_new
return x, y
Brown-Conrady 왜곡 공식을 다시 한 번 보자면 다음과 같다:
여기서 우리는
오른쪽에
따라서 코드 내에서 x와 y에 매개변수로 전달된 왜곡된 좌표 xd, yd를 초기값으로 사용한다.
최대 20번 반복되지만, 보통 5~10번 반복되면 충분히 근사값을 얻을 수 있다.
이후 코드는 수식과 같고, 결과 값을 다시 x, y 변수에 대입하여 값을 변경하면서 근사값을 구한다.
또한 5~10번 반복했을 때 충분히 근사치를 구할 수 있다면, 남은 반복 작업은 굳이 수행하지 않아도 되므로 이전 x, y의 값이 변경될 값과 절대적인 오차가 1e-8 이하라면 해당 반복문을 종료해도 된다.
그러나 해당 공식은 왜곡 계수(