[openCV] Image Geometric Transformation (2)
openCV

[openCV] Image Geometric Transformation (2)

2021/02/20 - [openCV] - [openCV] Image Geometric Transformation (1)

 

[openCV] Image Geometric Transformation (1)

오늘은 openCV에서 이미지의 기하학적 변환에 대해서 정리를 해보려고 한다. 사실 제목에서도 알 수 있듯이 기하학적인 요소가 들어가 있어서 함수에 대해서 심화적인 이해를 하기 위해서는 수학

confidence-10211.tistory.com

저번에 정리를 다 못 끝낸 이미지의 기하학적 변환에 대한 이야기를 마무리 해보자

 

1. Affine Transformation

 

Affine Transformation이란?


직선, 길이의 비, 평행성을 보존하면서 Warping 해준다.

- Affine Transformation의 변환 행렬을 찾으려면 입력 이미지의 3점 필요 -


우욱..

우선 Affine Transformation에서는 변환 행렬을 만들 때 3개의 점을 필요로 한다.

 

Affine 변환은 평행성을 유지 하기 때문에 오른쪽 상단의 좌표가 하단으로 이동을 하였다면

오른쪽 하단의 좌표 역시 동일하게 하단으로 이동하게 된다.

 

Affine Transformation 행렬

 

x' = ( a * x ) + ( b * y ) + e

y ' = ( c * x ) + ( d * y ) + f

 

 

위와 같은 식을 이용해서 이미지가 Affine Transformation이 된다.

 

Affine Transformation의 행렬은 미지수가 6개이므로 식이 6개가 필요하고

좌표가 3개가 있으면 된다.

 

openCV에서는 두 이미지 간에 대응하는 세 점을 안다면 getAffineTransformation 함수를 사용하여서

Affine Transformation 행렬을 얻을 수 있다.

 

변환 행렬을 얻었다면 warpAffine 함수로 이미지에 적용한다.

 

Affine Transformation 행렬을 만들때 사용하는 Vertex Position을 지정할 때에는

위와 같은 순서로 지정한다.

 


1. Affine Transformation 할 이미지 지정

2. getAffineTransform 함수를 사용하여서 변환 행렬 생성

3. warpAffine 함수로 이미지에 Affine Transformation 적용


이제 코드를 살펴보자

 

import numpy as np
import cv2

#Mouse Position
point_list = []

#CallBack Function
def mouse_callback(event, x, y, flags, param):

    if event == cv2.EVENT_LBUTTONDOWN:

        print(x, y)

        #Store The Position
        point_list.append((x,y))
        cv2.circle(img_color, (x,y), 3, (0, 0, 255), -1)

#Register The CallBack Function
cv2.namedWindow('source')
cv2.setMouseCallback('source', mouse_callback)

img_color = cv2.imread("./test.png")

while True:
    cv2.imshow('source', img_color)

    if cv2.waitKey(1) == 32:
        break

height, weight = img_color.shape[:2]

#Original Position || Original Position + 100
pts1 = np.float32([point_list[0], point_list[1], point_list[2]])
pts2 = np.float32([point_list[0], point_list[1], point_list[2]])
pts2[1][1] += 100

# for i in range(3):
#     print(point_list[i])

#Make The Transformation
M = cv2.getAffineTransform(pts1, pts2)

#Affine
img_result = cv2.warpAffine(img_color, M, (weight, height))

cv2.imshow('result', img_result)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

어렵지 않으니 하나하나 보자

 

#Mouse Position
point_list = []

마우스의 Position이 들어갈 Array이다.

 

#CallBack Function
def mouse_callback(event, x, y, flags, param):

    if event == cv2.EVENT_LBUTTONDOWN:

        print(x, y)

        #Store The Position
        point_list.append((x,y))
        cv2.circle(img_color, (x,y), 3, (0, 0, 255), -1)

마우스 CallBack 함수이다.

마우스를 누를 때마다 point_list에 저장한다.

 

#Register The CallBack Function
cv2.namedWindow('source')
cv2.setMouseCallback('source', mouse_callback)

'source' window에 mouse_callback 함수를 등록한다.

 

while True:
    cv2.imshow('source', img_color)

    if cv2.waitKey(1) == 32:
        break

반복하면서 좌표를 찍을 수 있도록 한다.

 

만약 SpaceBar (32)를 누르면 반복문이 종료된다.

 

height, weight = img_color.shape[:2]

이미지의 너비와 높이를 가져온다.

 

#Original Position || Original Position + 100
pts1 = np.float32([point_list[0], point_list[1], point_list[2]])
pts2 = np.float32([point_list[0], point_list[1], point_list[2]])
pts2[1][1] += 100

변환 행렬을 만들기 전 pts1, pts2에 전에 클릭헀던 마우스 positon들을 대입해준다.

 

pts2에는 2번째 클릭했던 마우스 position의 y좌표에 100을 더해주어서

y좌표가 내려갈 수 있도록 한다.

 

#Make The Transformation
M = cv2.getAffineTransform(pts1, pts2)

cv2.getAffineTransform(src, dst)

 

src = 이동 전 Postion

dst = 이동 후 좌표

 

#Affine
img_result = cv2.warpAffine(img_color, M, (weight, height))

cv2.warpAffine으로 Affine 변환 적용

 

[ Result ]

original
Set The Position

 

Affine Transformation

 

 

2. Perspective Transformation

 

Perpective Transformation이란?

 


3차원 공간에 있는 물체를 2차원 평면에 올려 놓아 표현하는 것

 

- 이때 Perspective Transformation의 변환 행렬을 찾으려면 출력 이미지의 4개의 점 필요 -


 

진짜 끔찍하다

위 그림을 보자

 

Perspective Transformation은 image1과 같이 3차원 공간에 있는 물체의

Vertex Postion을 잡아주면 Image2와 같이 2차원 평면로 물체를 Transformation 해주게 된다.

 

여기서 Perspective 변환 행렬은 우리가 지정한 Vertex Postion이 된다는 것이다.

Perspective Transformation 행렬

x' = (x * p11) + (y * p12) + p13

y' = (x * p21) + (y * p22) + p23

w = p31 + p32 + 1

 

미지수가 8개이므로 8개의 식이 도출되며

4개의 점이 필요하다.

 

openCV에서는 두 이미지 간의 대응하는 네 점을 안다면 getPerspectiveTransform 함수를 사용해서

Perspective 변환 행렬을 얻을 수 있다.

 

getPerspectiveTransform 함수를 사용해서 Perspective 변환 행렬을 얻었다면

warpPerspective 함수를 사용해서 이미지에 실질적으로 적용한다.

 

Vertex Position 순서

우리가 Vertex Position을 지정할 때에는 위의 순서와 같이 지정한다.

 


1. 3차원 평면 이미지 지정

2. Vertex Position을 활용하여 Perspective 변환 행렬 지정

3. 만들어진 변환 행렬 이용하여서 warpPerspective 함수 이용해서 이미지 적용


 

코드를 한번 살펴보자

 

import cv2
import numpy as np

#Mouse Position Store this Variable
src = np.zeros([4, 2], dtype=np.float32)
idx = 0

#Call Back Function
def mouse_callback(event, x, y, flags, param):
    global point_list, idx

    #Store The Mouse Position
    if event == cv2.EVENT_LBUTTONDOWN:
        src[idx] = (x, y)
        idx = idx + 1

        print(x, y)
        cv2.circle(img_color, (x, y), 10, (0, 0, 255), -1)

#Register The Mouse CallBack Function
cv2.namedWindow('original')
cv2.setMouseCallback('original', mouse_callback)

#Read The Image
img_color = cv2.imread("./Perspective.jpg")
img_original = img_color.copy()

#Set The Position
while True:
    cv2.imshow('original', img_color)

    height, width = img_color.shape[:2]

    if cv2.waitKey(1) == 32:
        break

#2 Dimension Position
dst = np.float32([[0,0], [width, 0], [0, height], [width, height]])

#Perspective Transformation
M = cv2.getPerspectiveTransform(src, dst)

img_result = cv2.warpPerspective(img_original, M, (width, height))

cv2.imshow('result1', img_result)
cv2.waitKey(0)
cv2.destroyAllWindows()

뜯어서 자세히 살펴보자

 

src = np.zeros([4, 2], dtype=np.float32)
idx = 0

2행 4열짜리 numpy Array를 만들어준다

numpy Array에 Perspective 변환 행렬을 만들 때 사용할

Vertex Position이 들어간다.

 

#Call Back Function
def mouse_callback(event, x, y, flags, param):
    global point_list, idx

    #Store The Mouse Position
    if event == cv2.EVENT_LBUTTONDOWN:
        src[idx] = (x, y)
        idx = idx + 1

        print(x, y)
        cv2.circle(img_color, (x, y), 10, (0, 0, 255), -1)

마우스 CallBack 함수인데

그냥 마우스가 누른 좌표를 numpy array에 저장하는것이다.

 

#Register The Mouse CallBack Function
cv2.namedWindow('original')
cv2.setMouseCallback('original', mouse_callback)

'original' window에 마우스 CallBack 함수를 지정한다.

 

#Set The Position
while True:
    cv2.imshow('original', img_color)

    height, width = img_color.shape[:2]

    if cv2.waitKey(1) == 32:
        break

반복해서 'original' 창에 좌표를 찍을 수 있으며

만약 Space Bar (32)를 누르면 끝이 난다.

 

#2 Dimension Position
dst = np.float32([[0,0], [width, 0], [0, height], [width, height]])

2차원 평면 상의 좌표를 지정해준다.

 

좌표를 지정해주는 이유는 위 반복문에서 지정한 3차원 상의 좌표를

2차원 평면의 좌표에 mapping 해줘야 되기 때문이다.

 

#Perspective Transformation
M = cv2.getPerspectiveTransform(src, dst)

cv2.getPerspectiveTransform(src, dst)

src = Original Position

dst = Transeformation Position

 

cv2.getPerspectiveTransform 함수를 이용해서

Perspective 변환 행렬을 생성한다.

 

img_result = cv2.warpPerspective(img_original, M, (width, height))

cv2.warpPerspective 함수를 이용해서

변환 행렬을 기반으로 실제로 이미지에 적용한다.

 

[ Result ]

 

Original Image
Set The Vertex Position
Final Image

오늘도 끄-읕😶

'openCV' 카테고리의 다른 글

[openCV] Convolution & Mask (2)  (0) 2021.03.06
[openCV] Convolution & Mask (1)  (0) 2021.03.06
[openCV] Image Geometric Transformation (1)  (0) 2021.02.20
[openCV] ROI  (0) 2021.02.17
[openCV] Draw Function  (0) 2021.02.06