원근법에 관한 라이브러리 및 함수를 사용하여 원근 변환은 이미지를 면환하는 방법을 배운다.
이건 휴대폰으로 촬영한 사진인데 이 사진에서
위아래가 잘 보이게 하려면 어떻게 해야할까
그러기 위해서 윤곽을 잡아줘야하는데 처리하기 위해 이미지를 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]
cv2.getPerspectiveTransform(inputPts, outputPts):
위에 모서리 좌표들을 원하는 좌표로 설정해주는 코드
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)