앞선 내용에서 영상의 이동, 회전 및 스케일링에 대해 다룬 적이 있다.

 

이미지 변환 - 이동, 회전, 스케일링

1. What is OpenCV? OpenCV란 Computer Vision을 위한 프로그래밍 라이브러리이며, TensorFlow나 PyTorch등의 Deep Learning Framework를 지원한다. 자바, 매트립 등 다양한 언어에서 개발환경을 지원하며, 여기서..

dsbook.tistory.com

위와 같은 경우는 이미지의 변환 후에도 기존 형태가 유지되지만, 이미지에 뒤틀기(Wariping) 변환을 주면 기존 형태와는 다르게 나타난다.

 

1. Affine Transform

아핀 변환(Affine Transform)이란 이동이나 회전 등을 포함하는 변환으로, 길이의 비율과 평행성이 보존되며 cv2.getAffineTransform()을 통해 적용할 수 있다.

import cv2
import numpy as np

img = cv2.imread('image.jpg')
row, col = img.shape[:2]

# 변환 전, 후 좌표
pt1 = np.float32([[200, 50], [300, 50], [200, 300]])
pt2 = np.float32([[100, 70], [250, 70], [250, 200]])

cv2.circle(img, (200, 50), 10, (255, 0, 0), -1)
cv2.circle(img, (300, 50), 10, (0, 255, 0), -1)
cv2.circle(img, (200, 300), 10, (0, 0, 255), -1)

# Affine 변환행렬 계산 및 적용
matrix = cv2.getAffineTransform(pt1, pt2)
affine = cv2.warpAffine(img, matrix, (col, row))

cv2.imshow('Origin', img)
cv2.imshow('Affine', affine)
cv2.waitKey()
cv2.destroyAllWindows()

Affine 변환 전(좌) / 후(우)

Affine 변환을 하기 전과 후의 좌표를 생성해서 변환 전 좌표를 이미지에 표시부터 한다. 이후, cv2.getAffineTransform()을 통해 변환행렬을 계산해서 Affine 변환을 적용한 결과를 출력하면 이미지가 회전한 것만이 아니라 표시한 좌표의 크기가 변하는 뒤틀림도 발생하는 것을 확인할 수 있다. 

 

2. Perspective Transform

원근 변환(Perspective Transform)이란 원근감을 주는 변환이다. 우리는 3차원 세계에서 살고 있기 때문에 원근감을 느끼지만, 영상은 2차원이기 때문에 차원 간의 차이가 발생한다. 이러한 차이를 보정하기 위해 동차 좌표라는 좌표계를 사용한다. 

동차 좌표계는 기존 차수에 1개의 차원을 추가한 형태로 나타낸다. 예를 들어, 2차원 좌표 (x, y)에 대응하는 동차 좌표는 (wx, wy, w)로 표현하고, 이를 다시 2차원 좌표로 바꿀 때는 각 성분을 w로 나누고 w를 떼어 (x/w, y/w) 형태로 표현한다. 그래서 원근 변환을 하려면 (x, y, 1)의 좌표계가 필요하고 다음과 같은 변환행렬이 필요하다.

원근 변환 행렬을 계산해주는 함수는 cv2.getPerspectiveTransform()이며, 변환 전과 후의 4개의 좌표만 입력하면 된다. 또한, 원근 변환을 수행하는 함수로는 cv2.warpPerspective()가 있으며, 사용 방법은 cv2.warpAffine()과 같다.

import cv2
import numpy as np

img = cv2.imread('image.jpg')
row, col = img.shape[:2]

pt1 = np.float32([[0, 0], [0, row], [col, 0], [col, row]])
pt2 = np.float32([[150, 80], [100, row - 80],[col - 150, 80], [col - 100, row - 80]])

cv2.circle(img, (0, 0), 15, (255, 0, 0), -1)
cv2.circle(img, (0, row), 15, (0, 255, 0), -1)
cv2.circle(img, (col, 0), 15, (0, 0, 255), -1)
cv2.circle(img, (col, row), 15, (0, 0, 0), -1)

# 원근 변환행렬 계산 및 적용
matrix = cv2.getPerspectiveTransform(pt1, pt2)
perspective = cv2.warpPerspective(img, matrix, (col, row))

cv2.imshow('Origin', img)
cv2.imshow('Perspective', perspective)
cv2.waitKey()
cv2.destroyAllWindows()

원근 변환 전(좌) / 후(우)

 

3. 원근 변환 및 마우스 이벤트로 문서 스캔

마우스 클릭으로 이미지에 보이는 문서의 4개의 점을 지정하면 마치 스캔을 한 것처럼 나타낼 수도 있다.

import cv2
import numpy as np

img = cv2.imread('paper.jpg')
draw = img.copy()

# 클릭 수 세는 변수
count = 0

# 좌표 초기화
pt = np.zeros((4,2), dtype = np.float32)

def Mouse(event, x, y, flag, param):
    global count # global variance
    if event == cv2.EVENT_LBUTTONDOWN: # 왼쪽 버튼 누름
        cv2.circle(draw, (x,y), 5, (0,255,0), -1)
        cv2.imshow('Scan', draw)
        pt[count] = [x,y]
        count = count + 1
        if count == 4:
        	# 좌표 상하좌우 지정
            position = pt.sum(axis = 1)
            differ = np.diff(pt, axis = 1)
            top_L = pt[np.argmin(position)] # 좌측 상단
            bottom_R = pt[np.argmax(position)] # 우측 하단
            top_R = pt[np.argmin(differ)] # 우측 상단
            bottom_L = pt[np.argmax(differ)] # 좌측 하단
            
            # 변환 전 좌표
            pt1 = np.float32([top_L, top_R, bottom_R, bottom_L])
            
            # 스캔하는 폭, 높이 계산
            width1 = abs(bottom_R[0] - bottom_L[0])
            width2 = abs(bottom_R[1] - bottom_L[1])
            height1 = abs(top_R[0]-top_L[0])
            height2 = abs(top_R[1]-top_L[1])
            width = max([width1, width2])
            height = max([height1, height2])
            
            # 변환 후 좌표
            pt2 = np.float32([[0,0], [width-1,0], [width-1,height-1], [0,height-1]])
            
            # 원근 변환
            matrix = cv2.getPerspectiveTransform(pt1, pt2)
            result = cv2.warpPerspective(img, matrix, (width, height))
            cv2.imshow('Result', result)
cv2.imshow('Scan', img)
cv2.setMouseCallback('Scan', Mouse)
cv2.waitKey()
cv2.destroyAllWindows()

기존 이미지, 클릭한 부분, 결과 이미지(좌측부터)

마우스 이벤트 처리 함수에서 4개의 좌표를 받은 후에 좌표 간 거리와 폭, 높이를 계산해서 원근 변환으로 스캔한 효과를 냈다. 클릭하는 순서에 관게없이 원하는 부분을 스캔할 수 있다.

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기