컴퓨터 비전/openCV

[openCV] Image Transformation - Affine Transformation, Perspective Transformation

kdjames0930 2025. 12. 3. 16:14

오늘은 opencv 라이브러리를 활용해 이미지의 transformation을 해보겠다.

 

Affine Transformation과 Perspective Transformation 모두 한 이미지의 좌표를 다른 좌표로 변환하는 기하학적 변환이다. 둘의 차이점은 변환 가능한 형태와 변환의 자유도에 있다.

 

  • Affine Transformation

 우선 Affine Transformation은 이미지의 회전, 축소와 확대, 이동 등을 포함하는 변환이다. 변환 후에 원래 평행한 선들이 그대로 평행하게 유지된다는 특징이 있다. 이미지에 Affine Transformation을 적용하는 방법은 바로 행렬곱(Matrix Multiplication)을 활용하는 것이다. 그리고 행렬곱을 용이하게 하기 위해서 우리는 Homogenuous 좌표를 활용한다.

 Homogenuous 좌표는 (x, y)를 (x, y, 1)이렇게 표현하는 것이다. 이렇게 표현하는 것이 행렬곱에 어떻게 도움이 되길래 차원을 하나 늘려서 사용하는 것인지 살펴보자.

​만약 (x, y) 를 (x', y')로 변환시키고 싶다면 위와 같이 계산하면 된다. 행렬 A를 통해서는 회전하는 정도, 확대 및 축소하는 정도를 조절할 수 있다. 이미지를 평행이동하기 위해서는 행렬곱 외에도 b 벡터를 더해줘야 한다. 하지만 Homogeneous 좌표를 사용하면 모든 작업을 행렬곱 하나로 처리할 수 있다.

 

.이처럼 연산을 할 수 있기 때문에 Homogeneous 좌표를 사용하는 것이다. 공부해보니 이뿐만 아니라 나중에 설명할 Perspective Transformation 에는 더더욱 필수적이라는 사실을 알아냈다. 아무튼 이제 이미지에 Affine Transformation을 적용해보자! 

 

img = cv2.imread("/content/drive/MyDrive/Dataset/opencv/Ganeshji.webp")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

center = (img_rgb.shape[1] // 2, img_rgb.shape[0] // 2)
angle = 30
scale = 1
rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)
print("Rotation Matrix")
print(rotation_matrix)
rotated_img = cv2.warpAffine(img_rgb, rotation_matrix, (img_rgb.shape[1], img_rgb.shape[0]))

"""
Rotation Matrix
[[   0.8660254     0.5        -106.50635095]
 [  -0.5           0.8660254   162.51288694]]
"""

 openCV의 warpAffine() 함수를 사용해 이미지에 Affine Transformation을 적용할 수 있다. 이때 Transform 계산에 사용할 2 x 3행렬을 파라미터로 넘겨줘야 한다. 행렬값들을 하나씩 지정해줄 수도 있지만, openCV에는 transformaiton 목적에 따른 행렬을 만들어주는 함수들이 존재한다. 회전의 경우 getRotationMatrix2D() 함수를 통해 회전할 중심 좌표, 회전각, 배율을 지정해주면 그에 맞는 Affine Matrix를 계산할 수 있다. 몇가지 다른 함수들을 살펴보자면

 

- getAffineTransform() : 대응하는 세개의 좌표쌍 [ (x,y) 와 (x', y') ] 을 통해 Affine Matrix 계산. Affine Transform에서는 2 x 3 행렬을 사용하므로 자유도가 6이고 최소 세개의 대응점이 필요하다.  

- getPerspectiveTransform() : 대응하는 네개의 좌표쌍  [ (x,y) 와 (x', y') ] 을 통해 Perspective Transform 계산. 

 

그림1. 이미지 Affine Transformation 결과

 

 의도한대로 이미지의 중심을 기준으로 반시계 방향으로 30° 회전시켰다.

 

 

  • Perspective Transformation

Perspective Transformation에서는 단순히 물체의 이동, 회전 뿐만 원근감을 표현하거나 평면을 다른 방향에서 바라본 것처럼 변환할 수 있다. 변환에 Homography라 불리는 3 x 3 행렬을 사용한다. 

Homography는 스케일 비례가 의미가 없다. 즉 모든 원소에 같은 상수 k값을 곱해도 같은 변환이 된다. 따라서 8의 자유도를 가지므로 4개의 대응점 좌표가 있다면 H 행렬을 구할 수 있다. 

위와 같이 계산을 하면 된다.

이론에서는 4개의 대응점이면 계산을 할 수 있지만, 실무에서는 노이즈 및 검출 오차가 존재하기에 더 많은 대응점을 사용하고 RANSAC(가장 지지하는 데이터가 많은 모델을 뽑는 파라미터 추정법)을 통해 H를 계산한다고 한다. 

 

img = cv2.imread("/content/drive/MyDrive/Dataset/opencv/Rabbit.jpg")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

h, w = img_rgb.shape[:2]

src = np.float32([ [700, 400], [670, 750], [1000, 500], [1050, 700]])
dst = np.float32([[100, 200], [100, 750], [900, 300], [1100, 700]])

M = cv2.getPerspectiveTransform(src, dst)
dst = cv2.warpPerspective(img_rgb, M, (w, h))

 

getPerspectiveTransform()을 통해 Homography를 계산하고 warpPerspective()를 사용해 이미지에 Perspective Transform을 진행했다.

그림2. 이미지 Perspective Transformation 결과

 

토끼를 조금 오른쪽에서 바라보는 시점으로 이미지를 변환시켰다.

Perspective Transform을 사용하면 Bird's eye view 도 쉽게 만들 수 있다.

 

img = cv2.imread("/content/drive/MyDrive/Dataset/opencv/road.jpg")

img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_copy = img_rgb.copy()

h, w = img_rgb.shape[:2]

cv2.circle(img_rgb, (70, 350), 5, (0, 0, 255))
cv2.circle(img_rgb, (145, 240), 5, (0, 0, 255))
cv2.circle(img_rgb, (320, 350), 5, (0, 0, 255))
cv2.circle(img_rgb, (240, 240), 5, (0, 0, 255))

src = np.float32([[70,350], [145, 240], [320, 350], [240, 240]])

dst = np.float32([[0, h], [0, 0], [w, h], [w, 0]
])

M = cv2.getPerspectiveTransform(src, dst)
img_dst = cv2.warpPerspective(img_copy, M, (w, h))

그림3. Perspective Transform을 통한 Bird's Eye View 생성

 

마치 도로를 위에서 본 것처럼 변환했다. Bird's Eye View는 자율주행에도 활용된다고 한다. 

반응형