OpenCV 비전 실무14장 · 영상 기하학 변환과 Affine, Perspective10 / 21← 이전목차다음 →MetaContents

14장. 영상 기하학 변환과 Affine, Perspective

세 점으로 아핀 변환

천막을 칠 때 말뚝 세 개를 박으면 천의 모양이 정해집니다. 말뚝 하나를 옮기면 천 전체가 따라 기울어집니다. 아핀 변환도 점 세 개가 어디로 갈지만 정하면 나머지 모든 점이 어디로 갈지 정해집니다. cv2.getAffineTransform 은 "이 세 점을 저 세 점으로"를 받아 2×3 표를 계산해 줍니다.

원본의 세 점과 옮길 세 점을 짝지어 네모를 평행사변형으로 기울이는 그림

왼쪽의 세 점(빨강, 초록, 주황)을 오른쪽의 세 점으로 옮기도록 정했습니다. 위의 두 점은 오른쪽으로 30칸, 아래 점은 왼쪽으로 30칸 옮겨, 네모가 평행사변형으로 기울었습니다(기울이기, shear). 정하지 않은 넷째 점(파랑)도 저절로 따라갑니다. 여기서 말뚝 세 개가 세 쌍의 점이고, 천 전체가 그림의 모든 칸입니다.

아핀 변환이 지키는 것과 바꾸는 것

지키는 것바꿀 수 있는 것
곧은 선은 곧은 선으로길이
평행한 선은 평행한 선으로각도(직각이 비스듬해질 수 있다)
한 선 위의 점들의 순서와 간격 비율넓이

평행한 선이 평행하게 남으므로, 네모는 평행사변형까지만 바뀝니다. 위가 좁고 아래가 넓은 사다리꼴로는 바꿀 수 없습니다. 그것은 다음 절의 원근 변환이 합니다.

넷째 점을 손으로 짐작하기

평행사변형에서는 "넷째 점 = 둘째 점 + 셋째 점 − 첫째 점"입니다. 왼쪽 위 (40, 40) → (70, 40), 오른쪽 위 (280, 40) → (310, 40), 왼쪽 아래 (40, 200) → (10, 200) 이면, 오른쪽 아래 (280, 200) 은 (310 + 10 − 70, 40 + 200 − 40) = (250, 200) 으로 갑니다.

짧은 예시

Python
import cv2
import numpy as np

img = cv2.imread("ch14_grid.png")
h, w = img.shape[:2]

# 원본의 세 점: 왼쪽 위, 오른쪽 위, 왼쪽 아래 색 점 (x, y)
src = np.float32([[40, 40], [280, 40], [40, 200]])
# 옮길 세 점: 위는 오른쪽으로 30, 아래는 왼쪽으로 30
dst = np.float32([[70, 40], [310, 40], [10, 200]])

# cv2.getAffineTransform(원본 세 점, 옮길 세 점) → 2×3 표
M = cv2.getAffineTransform(src, dst)
print(M.round(3))
# 결과: [[ 1.    -0.375 45.   ]
#        [-0.     1.     0.   ]]   ← -0. 은 0 과 같다(소수 계산의 부호만 남은 것)

# 넷째 점 (280, 200) 이 가는 곳: 표에 곱해 더한다
x, y = 280, 200
print(float(M[0, 0] * x + M[0, 1] * y + M[0, 2]), float(M[1, 0] * x + M[1, 1] * y + M[1, 2]))   # 결과: 250.0 200.0

sheared = cv2.warpAffine(img, M, (w, h), borderValue=(255, 255, 255))

표의 윗줄 둘째 칸 −0.375 가 기울이기를 만듭니다. 새 x 에 y 의 −0.375 배가 더해지므로, 아래로 갈수록(y 가 클수록) 왼쪽으로 밀립니다. 160칸 내려가면 160 × 0.375 = 60칸 밀립니다. 위 두 점이 오른쪽으로 30, 아래 점이 왼쪽으로 30 이니 차이가 60 입니다.

주의할 점

  • 세 점은 한 줄 위에 있으면 안 됩니다. 한 줄 위의 세 점으로는 천이 펴지지 않아 표를 계산할 수 없습니다.
  • 원본과 옮길 점의 순서는 짝이 맞아야 합니다. 첫째 점은 첫째 점으로 갑니다.
  • 점 목록은 np.float32 로 줍니다.
← 이전 절14장 · 회전다음 절 →14장 · 원근 변환