openCv page Dewarp 중심이론 설명

개요

구부러진 책을 펴기 위해서는 몇가지 모델링 및 가정이 필요합니다.

간단한 요점은 다음과 같습니다.

  • 3D상의 책은 높이(z) 0에 놓여 있는 평평한 종이라고 가정합니다.
  • 가정한 3D상의 좌표를 기준으로 회전벡터(r), 평행이동벡터(T)를 구합니다.
  • 책의 구부러짐을 3차원 방정식으로 표현된다고 가정합니다.
  • 3차원 방정식은 f(0)=0, f(1)=0 으로 0~1사이의 그래프라고 가정하고 두개의 기울기 f’(0)=α, f’(1)=β라고 정합니다.
  • α, β의 값에 따라서 3차원 방정식의 모양(책의 구부러진 모양과 동일하다 가정)이 결정됩니다.
  • 회전벡터 r, 평행이동벡터 t, 두개의 기울기 α, β를 통해 얻은 z값을 가지고 카메라 재투영을 통해 이미지 좌표계의 점을 구합니다.
  • 원래의 이미지 좌표계의 점 (x,y)와 위에서 구한 이미지 좌표계의 점 (x’, y’)와 비교합니다.
  • 두개의 비교(거리차)가 최소가 되는 α, β의 값을 구합니다.
  • 구한 α, β를 통해 z값을 구하고 평평한 종이라고 가정했을때 구한 회전벡터(r), 평행이동벡터(T)를 가지고 이미지 좌표계를 구합니다.
  • 구한 이미지 좌표계로 픽셀을 이동(remap)합니다. 이동한 이미지는 평평한 이미지가 됩니다.

위의 내용은 글로는 이해하기 힘든 부분이 많습니다.

아래에서 그림과 함께 하나하나 설명하도록 하겠습니다.

왜곡 가정

이미지 처리 기술중 카메라의 왜곡을 보정하는 기술이 존재합니다.

보통은 아래 그림처럼 카메라 렌즈에 의한 구부러진 왜곡을 실제 모습으로 펴기 위해 사용되는 기술입니다.

카메라 왜곡 보정

구부러진 책을 펴기위해 위의 왜곡 보정기술을 응용합니다.

원리는 간단합니다.

“나의 책은 원래 평평한 평면상에 펴져있는 상태로 놓여져 있지만 카메라 때문에 왜곡이 일어나서 구부러져 보일 뿐이다”라고 가정하는 것입니다.

평면상에 놓여진 가정된 3D 좌표는 아래와 같습니다.

[0, 0, 0], [page_width, 0, 0], [page_width, page_height, 0], [0, page_height, 0]

평평한 책의 좌표

높이가 없는 종이라고 가정하였기 때문에 z축은 모두 0이 됩니다. 그러고 x,y축의 좌표는 책의 높이와 넓이가 됩니다.

가정된 3D 좌표상의 좌표 [0, 0, 0], [page_width, 0, 0], [page_width, page_height, 0], [0, page_height, 0]와 이미지 평면상의 좌표 [0,0], [page_width, 0], [page_width, page_height], [0, page_height]을 가지고 회전 벡터(R)평행이동 벡터(t)를 취득할 수 있습니다.(cv::solvePnP)

Camera Coordinate System

개념 설명을 위해 Camera Coordinate System을 간단히 설명하면 3가지 개념이 존재합니다. 카메라 좌표계, 이미지 평면, 월드 좌표계입니다.

카메라 좌표계는 카메라를 기준으로 한 좌표계입니다. 카메라 좌표계는카메라의 초점(렌즈의 중심)을 원점, 카메라의 정면 광학축 방향을 Z축, 카메라 아래쪽 방향을 Y축, 오른쪽 방향을 X축으로 잡습니다.

카메라 좌표계

픽셀 좌표계의 x축, y축에 의해 결정되는 평면을 이미지 평면 (image plane)이라 부릅니다. 픽셀 좌표계는 우리가 실제 눈으로 보는 영상에 대한 좌표계로서 위 이미지의 왼쪽상단(left-top) 모서리를 원점, 오른쪽 방향을 x축 증가방향, 아래쪽 방향을 y축 증가방향으로 합니다.

월드 좌표계는 우리가 사물(물체)의 위치를 표현할 때 기준으로 삼는 좌표계입니다. 월드좌표계는 어디 하늘에서 주어져 있는 것이 아니라 문제에 따라서 우리가 임의로 잡아서 사용할 수 있는 좌표계입니다. 예를 들어, 자신의 안방 한쪽 모서리를 원점으로 잡고 한쪽 벽면 방향을 X축, 다른쪽 벽면 방향을 Y축, 하늘을 바라보는 방향을 Z축으로 잡을 수 있습니다.

위 개념을 크게 이해하실 필요는 없습니다.

다만 위에서 구한 회전벡터(R)과 평행이동 벡터(t)를 가지고 이미지 좌표계의 점(2D)을 월드좌표계(3D)로 변환 또는 3D → 2D 변환이 가능하다는 것을 기억하면 됩니다.

위에서 구한 두 벡터는 평평한 이미지를 위한 3D 좌표 변환을 위한 것입니다. 실제로 우리 책은 이미지 평면상에서 구부러져 보이고 있습니다. 이를 보정하기 위해서는 현재의 정확한 3D 좌표가 필요합니다.

책의 구부러짐을 3차원 방정식으로 표현된다고 가정

현재 우리가 가지고 있는 정보는 구부러진 책의 이미지의 이미지 좌표계(x,y)입니다. 하지만 평평한 이미지로 보정을 위해서는 높이 z를 포함한 월드 좌표계의 정보가 필요합니다.

구부러진 책의 좌표를 보면 아래 그림과 같습니다.

구부러진 책의 좌표

여기서 우리는 책의 페이지는 구부러진 상태로 평면에 놓여 있다고 가정합니다.

평면에 놓여져 있는 책페이지

여기서 우리는 책의 y축에 따른 z의 값은 일정하다고 가정합니다.

위 가정에 따라 y축을 지우면 아래 그림과 같은 곡선이 표시됩니다.
여기서 책의 넓이는 1로 표준화 합니다.

표준화한 책의 곡선 그래프

여기서 또한가지 중요한 가정이 필요합니다.

위의 그래프가 3차원 그래프라고 가정하는 것입니다.

위 그래프는 f(0)=0, f(1)=0 입니다.

또한 x가 0일때의 기울기 f'(0)=α, x가 1일때의 기울기 f'(1)=β라고 정합니다.

3차 방정식 ax³+bx²+cx+d=0 과 f(0)=0, f(1)=0, f'(0)=α, f'(1)=β를 계산합니다.

f(0)= 0이기 때문에 d=0이 됩니다.

f(1)=0이기 때문에 a+b+c=0이 됩니다.

f'(x)는 3ax²+2bx+c입니다.

f'(0)=α 이므로 c=α 입니다.

f'(1)=β 이므로 3a+2b+α=β 입니다.

위 식을 풀면 a = α+β, b = -2α-β가 됩니다.

최종적으로 (α+β)x³-(2α+β)x²+αx=0의 방정식을 얻을 수 있습니다.

위의 방정식을 α, β의 변화에 따른 그래프를 그려보면 아래와 같습니다.

α, β의 변화에 따른 그래프

위의 곡선은 책이 구부러짐과 많이 비슷합니다. 그렇기 때문에 모든 책의 구부러짐은 3차 방정식의 그래프로 표현할 수 있다라고 가정하는 것입니다.

카메라 재투영 오류(reprojection error)

이제 우리는 특정 α, β를 지정하면 거기에 따른 높이(z)를 구할 수 있습니다.

이미 우리는 이미지 평면상에서 점 (x,y)를 알고 있습니다. 거기에 위 3차원 방정식에 x값을 대입하면 높이 z를 구할 수 있고 최종적으로 3D 좌표점(x,y,z)를 구할 수 있습니다.

α, β를 구하기 위해서 카메라 재투영의 오류를 최소화하는 값을 구해야 합니다.

카메라 재투영이란 쉽게 말해 월드 좌표계의 3D 좌표를 이미지 평면상의 2D 좌표로 변환하는 것입니다.

여기서 필요한 것은 월드 좌표계의 3D 좌표와 회전벡터(r), 평행이동 벡터(T)입니다.

우리는 앞서 회전벡터(r), 평행이동 벡터(T)를 구했습니다. 여기서 중요한 것은 회전벡터(r), 평행이동 벡터(T)이 이미지 자체가 평평하다 라고 가정하고 구한 값이라는 것입니다.

여기에 따라 우리는 α, β을 변환하며 z값을 구할 수 있습니다. 구한 z값의 3d 좌표 (x,y,z)와 회전벡터(r), 평행이동 벡터(T)을 가지고 재투영 기술(cv::projectPoints)을 사용하여 이미지좌표 (x’, y’)를 구합니다.

위에서 구한 이미지좌표 (x’,y’)와 원래의 이미지 좌표(x,y)를 비교합니다.

아래 이미지는 α, β가 0일때 빨간점은 원래의 이미지 좌표, 파란점은 재투영 후 이미지 좌표를 표시한 것입니다.

원래 좌표와 재투영 후 좌표의 차이

위를 보면 원래의 좌표와 재투영 후의 좌표가 차이가 나는 것을 알 수 있습니다.

따라서 우리는 α, β를 임의의 값으로 조정 후 원래의 좌표와 재투영 후의 좌표가 차이가 최소가 되는 α, β값을 찾아야 합니다.

이를 위해서는 최소값을 찾아주는 파이썬의 기술인 scipy.optimize.minimizePowell방식을 사용할 수 있습니다.

해당 방법은 간단히 말해 임의의 α, β값을 넣어준 후 구한 재투영 좌표와 원래의 좌표의 차를 계속 구해 최소가 되는 어떠한 α, β를 반복 작업을 통해 구하는 것입니다.

이를 통해 구한 α, β의 값으로 재투영을 하면 아래와 같이 두 좌표의 차이가 거의 사라진 것을 볼 수 있습니다.

최적화 후 원래 좌표와 재투영 후 좌표의 차이

이제 우리는 α, β의 값을 구했으므로 x,y에 따른 z값을 구할 수 있습니다. 이것은 3D좌표를 구할 수 있다는 의미입니다.

이미지 평탄화 작업

앞서 말한 것 처럼 우리가 처음에 구했던 회전, 평행이동 벡터는 이미지 자체가 굴곡이 없이 평행하다고 가정하고 구한 값입니다. 그렇기 때문에 이미지 좌표(x,y)와 위에서 구한 α, β 값으로 구한 z값을 합한 3D좌표 (x,y,z)와 회전,평행이동 벡터를 가지고 재투영 기술(cv::projectPoints)을 사용하면 이미지 평면상의 2D 좌표 (x’, y’)를 구할 수 있습니다.

이와같이 모든 점(픽셀)에 대해서 (x,y) → (x’,y’)로 이동을 해줍니다. 이를 위해 리맵기술(cv::remap)을 사용할 수 있습니다.

위와 같은 작업을 한다면 우리는 굴곡된 이미지(x,y)에서 평탄화된 이미지 (x’,y’)를 얻을 수 있습니다.

이미지 평탄화 작업 전후의 이미지

공유하기