원근법적 변환 - Perspective Transforms

김성빈·2024년 5월 3일
0

Modern Computer Vision

목록 보기
21/117

목표

1. OpenCV의 get Perspective Transform 사용

2. 윤곽선(contours) 찾기를 사용하여 모서리를 찾고 관점 전환 자동화

원근법에 관한 라이브러리 및 함수를 사용하여 원근 변환은 이미지를 면환하는 방법을 배운다.

1. OpenCV의 get Perspective Transform 사용

이건 휴대폰으로 촬영한 사진인데 이 사진에서

위아래가 잘 보이게 하려면 어떻게 해야할까

그러기 위해서 윤곽을 잡아줘야하는데 처리하기 위해 이미지를 grayscale 처리를 한 후 이진화해준다.

그리고 인식한 윤곽에다가 색을 칠해주면 아래와 같이 나온다.

cv2.drawContours(image, contours, -1, (0,255,0), thickness = 2)
imshow('Contours overlaid on original image', image)

사진 밑에를 잘보면 의자 끝부분에 윤곽을 잡은것이 보이는데 먼지나 빛반사로 인해 잘못 인식한 부분이며

실제로도 윤곽선의 개수를 체크해보면
Number of Contours found = 54개가 나온다.

그러면 종이의 윤곽부분만 꺼내와서 사용해야 한다.

종이 윤곽 좌표 추출

종이의 윤곽만 사용하기 위해선 종이 윤곽의 각 모서리 4개의 좌표를 알아야 하는데,

cv2.approxPolyDP를 사용한다.

# 영역별로 큰 등고선에서 작은 등고선 정렬

sorted_contours = sorted(contours, key=cv2.contourArea, reverse=True)

# 윤곽을 넘기다
for cnt in sorted_contours:
	# 윤곽선의 근사치
	perimeter = cv2.arcLength(cnt, True)
	approx = cv2.approxPolyDP(cnt, 0.05 * perimeter, True)

	if len(approx) == 4:
		break

# 네 귀퉁이의 x,y 코디네이드
print("Our 4 corner points are:")
print(approx)

코드 실행 결과

4개의 좌표가 나오는데 위의 윤곽선의 4 꼭지점의 좌표를 나타낸것을 눈으로 확인할수있다.

4개의 모서리값을 알았는데, 이걸 어떻게 사용할까?

우선 하고자하는것은 삐뚤어진 이미지를 똑바로 하는 작업이다.

이런 작업들은 휴대폰이나 컴퓨터에서 직접 이미지를 조절해본 사람이면 알것이다.

# 여기서 얻은 순서는 왼쪽 위, 왼쪽 아래, 오른쪽 아래, 오른쪽 위입니다
inputPts = np.float32(approx)

outputPts = np.float32([[0,0],
                       [0,800],
                       [500,800],
                       [500,0]])

# 변환 매트릭스 가져오기, M
M = cv2.getPerspectiveTransform(inputPts,outputPts)

# Warp Perspective를 사용하여 변환 Matrix 적용
dst = cv2.warpPerspective(image, M, (500,800))

imshow("Perspective", dst)

해당 코드가 위의 삐뚤어진 이미지를 똑바로 보이게 해준다.

inputPts = np.float32(approx)

outputPts = np.float32([[0,0], # 좌측 상단
                       [0,800], # 좌측 하단
                       [500,800], # 우측 하단
                       [500,0]]) # 우측 상단

처음에 inputPts를 아까 윤곽선에서 추출한 4개 의 모서리를 매체에 담아줬고,

outputPts에는 우리가 원하는 이미지의 모서리4개를 넣어줬다.

이미지의 모서리에 다시 돌아와서 outputPts와 비교해서 정의를 해주면 아래와 같다.

[[[326  15]] # 좌측 상단

 [[ 83 617]] # 좌측 하단

 [[531 779]] # 우측 하단

 [[697 211]]] # 우측 상단

결론은 이미지의 모서리들을 우리가 놓아야할 모서리들(outputPts)에 옮겨주면 된다.

[326 15] > [0,0]

[ 83 617] > [0,800]

[531 779] > [500,800]

[697 211] > [500,0]

  1. cv2.getPerspectiveTransform(inputPts, outputPts):
    위에 모서리 좌표들을 원하는 좌표로 설정해주는 코드

  2. cv2.warpPerspective(image, M, (500,800)):
    이미지에 원근 변환을 적용, image는 원본 이미지이고, M은 변환 행렬로 원하는 좌표로 변경된 행렬이다.
    (500, 800)은 출력 이미지의 크기이며 변환된 이미지가 이 크기로 조정된다.

변환한 이미지에 윤곽선 색상 없는 버전

import cv2
import numpy as np

# 이미지 읽기
image = cv2.imread('images/scan.jpg')

# 그레이스케일로 변환
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 이진화 처리
_, th2 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# 윤곽선 찾기
contours, _ = cv2.findContours(th2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 넓이를 기준으로 윤곽선 정렬
sorted_contours = sorted(contours, key=cv2.contourArea, reverse=True)

# 꼭지점이 4개인 가장 큰 윤곽선 찾기
for cnt in sorted_contours:
    perimeter = cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, 0.05 * perimeter, True)
    
    if len(approx) == 4:
        break

# 원근 변환을 위한 출력 점 정의
output_pts = np.float32([[0, 0], [0, 800], [500, 800], [500, 0]])

# 변환 행렬 구하기
M = cv2.getPerspectiveTransform(approx.astype(np.float32), output_pts)

# 원근 변환 적용
dst = cv2.warpPerspective(image, M, (500, 800))

# 결과 표시
imshow("Perspective", dst)
profile
감사합니다. https://www.youtube.com/channel/UCxlkiu9_aWijoD7BannNM7w

0개의 댓글