14장. 영상 기하학 변환과 Affine, Perspective
회전
회전목마를 떠올려 봅시다. 가운데 기둥은 제자리에 있고, 말들은 기둥을 중심으로 원을 그리며 돕니다. 기둥에서 멀리 있는 말은 크게, 가까운 말은 작게 돕니다. 그림을 돌릴 때도 중심점 하나를 정하고, 모든 칸이 그 점을 중심으로 같은 각도만큼 원 위를 돕니다.
왼쪽은 중심점 둘레의 점이 원 위를 30도씩 도는 모습입니다. 가운데는 모눈 그림을 원본 크기 그대로 30도 돌린 결과로, 네 모서리가 그림 밖으로 나가 잘리고 빈 곳은 검게 칠해졌습니다. 오른쪽은 결과 그림을 키워 모서리까지 모두 담았습니다. 여기서 회전목마의 기둥이 center, 도는 각도가 angle 입니다.
cv2.getRotationMatrix2D 의 인자
돌리는 2×3 표의 숫자는 각도에 따라 복잡하게 바뀝니다. 손으로 계산하지 않고 cv2.getRotationMatrix2D(get rotation matrix 2D, 2차원 회전 표 얻기)가 계산해 줍니다.
그림의 세 상자가 이 함수의 세 인자입니다. 각도는 양수면 반시계 방향으로 돕니다.
| 인자 | 뜻 | 보통 값 |
|---|---|---|
center | 돌 때 제자리에 있는 중심점 (x, y) | 그림 한가운데 (w / 2, h / 2) |
angle | 돌릴 각도(도). 양수는 반시계 방향, 음수는 시계 방향 | 30, -15 |
scale | 돌리면서 키울 배율. 1 이면 크기 그대로 | 1.0 |
90도를 돌리면
중심을 (0, 0) 으로, 각도를 90 으로 주면 표는 [[0, 1, 0], [-1, 0, 0]] 이 됩니다. 점 (1, 0) 에 곱해 더하면 새 x = 0 × 1 + 1 × 0 = 0, 새 y = −1 × 1 + 0 × 0 = −1 입니다. 오른쪽에 있던 점이 위로(그림의 y 는 아래로 갈수록 커지므로 −1 은 위) 갔습니다. 화면에서 보면 반시계 방향으로 90도 돈 것입니다.
돌려도 안 잘리게 하기
원본 크기 그대로 돌리면 모서리가 잘립니다. 안 잘리게 하려면 돌린 그림을 모두 담는 새 가로·세로를 계산하고, 표의 셋째 칸(옮길 거리)을 고쳐 그림을 새 한가운데로 옮깁니다. 새 가로·세로는 표의 첫째·둘째 칸 숫자로 계산할 수 있어서 코드 두 줄이면 됩니다(실습 4). 30도 돌린 320×240 그림은 397×368 이 되어야 모두 담깁니다.
짧은 예시
import cv2
img = cv2.imread("ch14_grid.png")
h, w = img.shape[:2]
# 그림 한가운데 (160, 120) 를 중심으로 30도(반시계 방향), 크기 그대로
M = cv2.getRotationMatrix2D((w / 2, h / 2), 30, 1.0)
print(M.round(3))
# 결과: [[ 0.866 0.5 -38.564]
# [ -0.5 0.866 96.077]]
# borderValue: 빈 곳을 흰색으로 칠한다
rotated = cv2.warpAffine(img, M, (w, h), borderValue=(255, 255, 255))
print(rotated.shape) # 결과: (240, 320, 3) → 크기 그대로, 모서리는 잘린다
# 중심점은 돌아도 제자리다: (160, 120) 에 표를 곱해 더해 본다
print(round(float(M[0, 0] * 160 + M[0, 1] * 120 + M[0, 2]), 1),
round(float(M[1, 0] * 160 + M[1, 1] * 120 + M[1, 2]), 1)) # 결과: 160.0 120.0표의 숫자 0.866 과 0.5 는 30도에 맞춰 함수가 계산한 값입니다. 셋째 칸(−38.564, 96.077)은 중심점이 제자리에 있도록 함수가 맞춘 옮길 거리입니다.
주의할 점
- OpenCV 의 각도는 양수가 반시계 방향입니다. 시계 방향으로 돌리려면 음수를 줍니다.
- 90도, 180도, 270도만 돌릴 때는
cv2.rotate가 낫습니다. 칸을 그대로 옮기므로 값이 바뀌지 않고 잘리지도 않습니다. - 30도처럼 비스듬히 돌리면 칸이 정확히 칸 위에 떨어지지 않아 보간이 일어납니다. 여러 번 나눠 돌리면 그때마다 조금씩 흐려지므로, 한 번에 돌립니다.