일차변환으로 이미지 회전하기

일전에 네 자리 비밀번호 빈도[1]와 뱀 게임의 winning solution[2]에 대한 포스팅을 하면서 소개한 적이 있는 DataGenetics 블로그에서 또 재미있는 글[3]이 올라와서 포스팅한다. 이 포스팅은 DataGenetics 블로그의 포스팅의 내용[3]을 거의 그대로 가지고 온 것이다.

많은 이미지 에디터에서 임의의 각도로 이미지를 회전시킬 수 있는 기능을 제공한다. 어떻게 해야 잘 회전시킬 수 있을 지 생각해보자.

w
g22비트맵 이미지의 경우 각각의 픽셀은 삼원색 RGB의 색상에 대한 정보를 가지고 있다. 어느 한 픽셀의 위치가 직교좌표계로서 주어진다면 x좌표와 y좌표의 값으로 위치가 결정된다. 이 상황에서 이미지를 회전하려면 회전중심을 좌표계의 원점으로 취급할 때 다음과 같은 일차변환으로 회전 이후의 좌표가 결정된다. DataGenetics 블로그[3]에는 수식이 약간 잘못되어 있다.

\displaystyle \begin{pmatrix}x' \\ y'\end{pmatrix} = \begin{pmatrix}\cos\theta & -\sin\theta \\ \sin\theta & \cos\theta\end{pmatrix} \begin{pmatrix}x \\ y \end{pmatrix}

정상적인 이과 고교 교육과정을 이수한 사람이라면 모두 알고 있는-_- 회전변환이다. 그런데 이 방법 그대로 이미지를 회전하면 이렇게 된다.
nbsout
이런! 회전은 되었는데, 촘촘하지 못하고 구멍이 보인다. 왜 이런 일이 발생했을까? 각 픽셀은 정수좌표를 가지지만 회전변환의 결과는 실수가 된다. 픽셀의 좌표는 항상 정수이므로 반올림하는 과정에서 두 픽셀이 같은 위치에 배당되고 아무것도 오지 않는 빈 자리가 발생하게 된다.

이 문제를 해결하는 다양한 방법이 있다고 하는데, 그 중 이런 방법도 있다. 회전 이후의 픽셀에 대해 거꾸로 역변환을 시킨다. 원본 이미지에 걸치는 네 개의 픽셀에 대해 면적에 대한 색의 가중평균을 구해서 색을 결정한다. 본인의 추측이지만 아마 포토샵의 free transform 기능은 이런 방법을 쓸 듯 싶다. ㅋ

DataGenetics 블로그[3]에는 이보다 더 재미있는 방법이 소개되어 있는데, 다음과 같다. 먼저 회전변환을 다음과 같이 세 일차변환으로 분리한다. 여기서도 DataGenetics 블로그에 수식이 약간 잘못 되어 있다.

\displaystyle \begin{pmatrix}x' \\ y'\end{pmatrix} = \begin{pmatrix} 1 & -\tan\frac{\theta}{2} \\ 0 & 1 \end{pmatrix} \begin{pmatrix} 1 & 0 \\ \sin\theta & 1\end{pmatrix} \begin{pmatrix} 1 & -\tan\frac{\theta}{2} \\ 0 & 1 \end{pmatrix} \begin{pmatrix}x \\ y \end{pmatrix}

위 세 행렬을 곱하면 원래 회전변환 행렬과 동일한 행렬이 된다. 그 증명은 배각의 공식 등의 삼각함수 공식을 적절히 섞으면 되는데, 연습문제로 풀어보시라. ㅋ 사실 선형대수학에서 행렬을 upper triangular matrix와 lower triangular matrix로 분해하는 것을 LU decomposion이라고 하는데, 이 경우에도 굳이 이름을 붙이자면 ULU decomposion 정도 될 것 같다. ㅋ

각각의 행렬은 이미지를 일그러뜨리게 되는데, 위 세 matrix의 determinat가 모두 1이므로 도형의 면적이 보존된다. 첫 번째와 세 번째 행렬은 동일하고 이미지를 좌우로 일그러뜨리고, 가운데 행렬은 상하로 일그러뜨린다. 아래 이미지를 보시라.
res
DataGenetics 블로그에서는 좌표축의 x좌표를 아래쪽 방향으로, y좌표를 우측 방향으로 쓰고 있는 듯 하다. 그래서 일그러뜨리는 방향이 반대로 가는 듯. 여하간 세 변환을 차례로 행하면 이미지의 구멍없이 깔끔한 회전이 된다.

이러한 방법으로 이미지 회전 기능이 없고 일그러뜨리는 기능만 있는 마이크로소프트 윈도우 그림판에서 임의의 각도로 이미지 회전이 가능하다. ㅎ

DataGenetics 블로그 포스팅[3]의 뒷부분에 마이크로소프트 윈도우즈 그림판으로 이미지를 임의의 각도로 회전하는 법을 시범적으로 보여줬는데, 일차변환에서 수학적으로 약간 수정을 가하였길래 그 부분을 보완설명해 보겠다. ㅋ

먼저 MS 그림판 소프트웨어는 윈도우8 에서도 여전히 임의의 각도로 회전하는 기능이 없다. (90도로 회전하는 기능은 있음) 하지만 그림을 늘리거나 일그러뜨리는 기능은 있는데, 이 기능을 이용해보자. 일전 포스팅에서 소개한 회전변환의 decomposition은 다음과 같다.

\displaystyle \begin{pmatrix} 1 & -\tan\frac{\theta}{2} \\ 0 & 1 \end{pmatrix} \begin{pmatrix} 1 & 0 \\ \sin\theta & 1\end{pmatrix} \begin{pmatrix} 1 & -\tan\frac{\theta}{2} \\ 0 & 1 \end{pmatrix}  = \begin{pmatrix}\cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{pmatrix}

여기서 가운데 행렬을 구현하기가 약간 곤란한데, 가운데 행렬 때문에 10도를 돌리고 싶다고 해서 10도만큼 기울이면 안된다. 가운데 행렬은 직선 y=1y = x\sin\theta +1 로 변환하는 행렬이므로 \tan^{-1}(\sin\theta) 만큼 그림판으로 기울여야 한다. 아 이걸 몰라서 한참 삽질했네-_- 나머지 두 행렬은 원하는 각도의 절반만큼 기울이는 행렬이다. 실제로 한 번 해 보자.

만약 40도로 그림을 회전하고 싶다면, 먼저 가로 기울이기로 -20도 기울여 첫번째 행렬로 표현되는 일차변환을 구현한다.
step1
가운데 행렬을 구현하기 위해 \tan^{-1}(\sin 40^{\circ})를 계산하여 결과인 라디안을 육십분법으로 환산하면 대략 32.73도가 된다. 그림판은 정수만 입력되므로 33도를 입력하자.
step2
마지막 행렬은 처음과 동일한 행렬이므로 같은 작업을 한 번 더 해준다.
step3
짜잔! 회전한 행렬이 완성된다. 이 방법은 DataGenetics 블로그에서 소개한 방법과 좀 다른데, 이는 DataGenetics 블로그[3]에서는 실컷 저 변환을 소개해주고 그림판에서는 정작 다음과 같은 변환으로 바꿔서 써먹기 때문이다.

\displaystyle \begin{pmatrix} 1 & 0 \\ \tan\theta & 1 \end{pmatrix} \begin{pmatrix} \cos\theta & 0 \\ 0 & \frac{1}{\cos\theta}\end{pmatrix} \begin{pmatrix} 1 & -\tan\theta \\ 0 & 1 \end{pmatrix}  = \begin{pmatrix}\cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{pmatrix}

뭐 이쪽이 LDU decomposition이니 만큼 더 자연스럽지 않나 하는 생각도 든다. 여하간 이것 때문에 설명에서 좌우로 늘리는 작업이 들어가고 기울이기 과정이 한 번 빠진다. 이것 때문에 이해가 안 돼서 한참 삽질했네… -_- DataGenetics 블로그[3]를 읽으실 때 참조하시길.

 


[1] 내 백과사전 네 자리 비밀번호 빈도 분석 2012년 9월 19일
[2] 내 백과사전 뱀 게임의 winning solution 2013년 4월 23일
[3] Rotating Images in DataGenetics

3 thoughts on “일차변환으로 이미지 회전하기

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중