투시변환(persective)
- 직사각형 형태의 영상을 임의의 입체감 있는 사각형 형태로 변환
- 원본 영상에 있는 직선은 결과 영상에서 그대로 유지되지않고 평행 관계가 깨질 수 있음
- 투시 변환은 보통 3*3 실수 행렬로 표현
cv2.getPerspectiveTransform(영상, 4개의 결과 좌표점) -> 투시;변환 행렬 만들어짐
cv2.warpPerspectibe(영상, 투시변환행렬, 결과영상크기)
예제로는 살짝 누워있는 이미지를 이용해서 그 이미지만 펴서 내가 변환할 사이즈에 맞춰서
변환해주는 예제를 활용했습니다.
import cv2
import numpy as np
img = cv2.imread('./pic.jpg')
#변환할 크기를 설정한것
w,h = 300,200
#꼭지점 좌표
#순서 : 왼쪽상단->우측상단->우측하단->좌측하단
srcQuad = np.array([[369, 172], [1228, 156], [1424, 846], [207, 801]], np.float32)
dstQuad = np.array([[0,0], [w,0], [w,h], [0,h]], np.float32)
pers= cv2.getPerspectiveTransform(srcQuad, dstQuad)
dst = cv2.warpPerspective(img, pers, (w, h))
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()
결과는 이렇게 잘 나오긴했는데 여기서 궁금한점이
왜 좌측 하단좌표가 왜 [0,0]이 될까 부터시작한 좌표가 이해가 가지않았습니다.
gpt 4.0 답변
핵심 개념:
이미지를 다룰 때 컴퓨터는 **왼쪽 상단을 (0, 0)**으로 정하고 시작해요. 이건 우리가 책을 읽을 때도 왼쪽 위에서 아래로 내려오며 읽는 것처럼 생각하면 돼요.
비교로 이해하기:
수학 좌표계: 우리가 수학에서 배우는 좌표계에서는 **왼쪽 하단이 (0, 0)**이에요. 그래서 y축은 위로 올라갈수록 값이 커져요.이미지 좌표계: 반면, 컴퓨터에서 이미지를 다룰 때는 **왼쪽 상단이 (0, 0)**이에요. 그래서 x축은 오른쪽으로, y축은 아래쪽으로 갈수록 값이 커져요. 책을 읽듯이 왼쪽 위에서 시작해서 오른쪽으로 이동하고, 한 줄이 끝나면 아래로 내려가면서 처리하는 것과 비슷해요.
다시 풀어서:
왼쪽 상단에서 시작하는 이유는, 컴퓨터 화면이나 이미지를 다룰 때는 맨 위부터 차례대로 아래로 내려오며 픽셀을 그리기 때문이에요.**왼쪽 상단이 (0, 0)**이 되고, 그다음 오른쪽으로 갈수록 x 값이 커지고, 아래로 갈수록 y 값이 커지는 거죠.
그림으로 생각해보면:
왼쪽 상단: 시작점이니까 (0, 0).오른쪽 상단: 가로로 x축이 커져서 (가로 길이, 0).왼쪽 하단: 세로로 y축이 커져서 (0, 세로 길이).오른쪽 하단: 가로와 세로 모두 커져서 (가로 길이, 세로 길이).
이렇게 이미지 좌표는 왼쪽 상단부터 시작해 오른쪽과 아래로 차례차례 커지게 됩니다.
dstQuad: 변환 후의 좌표를 정의하는 배열입니다.
이 배열에는 4개의 점이 필요해요:
왼쪽 상단: (0, 0)
오른쪽 상단: (w, 0)
오른쪽 하단: (w, h)
왼쪽 하단: (0, h)
결국
왼쪽 상단: (0, 0)
오른쪽 상단: (w, 0)
오른쪽 하단: (w, h)
왼쪽 하단: (0, h)
이렇게 암기해야 될 것 같습니다.
최종보스
사진을 넣고 점을넣고, 선을 그리고, 클릭하고, 영역을 지정한다음
엔터를 누르면 사진이 뿅! 하고 나타나게끔 하는 최종보스입니다.
보험청구셔루같은걸 구현한다고생각하면 신기했어요.
가이드라인을 보자면
1.창의 크기를 정하고, 영상(이미지)를 불러옴
2.원들을 양쪽 끝에 그리기
3.그 원의 가운데 좌표 구하기(circle)
4.각각 라인 그려주기
5. 마우스이벤트 드래그를 이용해서 원을 드래그시킴
6.roi를 이용한 enter를 누르면 창이 잘 됨
import cv2
import numpy as np
import sys
#roi함수
def drawROI(img, corners):
cpy = img.copy()
#색상
c1 = (192, 192, 255)
c2 = (128, 128, 255)
#4개 좌표에 동그라미 그리기
for pt in corners:
cv2.circle(cpy, tuple(pt.astype(int)), 25, c2, -1)
#직선 그리기, 튜플로 좌표 이용하기
cv2.line(cpy, tuple(corners[0].astype(int)), tuple(corners[1].astype(int)),c2, 2)
cv2.line(cpy, tuple(corners[1].astype(int)), tuple(corners[2].astype(int)),c2, 2)
cv2.line(cpy, tuple(corners[2].astype(int)), tuple(corners[3].astype(int)),c2, 2)
cv2.line(cpy, tuple(corners[3].astype(int)), tuple(corners[0].astype(int)),c2, 2)
return cpy
#전역변수 등록
#전좌표를 가지고있으려고 쓰는 함수
ptOld = None
#마우스이벤트 함수
def onMouse(event, x, y, flags, param):
global srcQuad, dragSrc, ptOld, img
if event == cv2.EVENT_LBUTTONDOWN:
for i in range(4):
#좌표와 좌표를 계산해주는 함수
if cv2.norm(srcQuad[i] - (x,y)) < 25:
dragSrc[i] = True
ptOld = (x,y)
break
if event == cv2.EVENT_LBUTTONUP:
for i in range(4):
dragSrc[i]= False
# 클릭 후 움직임
if event == cv2.EVENT_MOUSEMOVE:
for i in range(4):
if dragSrc[i]:
srcQuad[i] = (x,y)
cpy = drawROI(img, srcQuad)
cv2.imshow('img', cpy)
ptOld = (x,y)
break
img = cv2.imread('./namecard.jpg')
#높이 길이 알아오기
h,w = img.shape[:2]
#a4용지 크기 : 210*297mm
#높이
dh = 500
#가로
dw = round(dh * 297 / 210)
#이렇게하면 같은 비율로 만들어짐
#시작점 지정
srcQuad = np.array([[30, 30], [30, h-30], [w-30, h-30], [w-30, 30]], np.float32)
dstQuad = np.array([[0,0], [0, dh], [dw, dh],[dw, 0]], np.float32)
#원이 드레그 되고있는걸 확인할 수 있는 변수
dragSrc = [False, False, False, False]
#영상띄우기
#영상에다 원 그려주고 직선그려는것
disp = drawROI(img, srcQuad)
cv2.imshow('img', disp)
cv2.setMouseCallback('img', onMouse)
while True:
key = cv2.waitKey()
if key == 13:
break
elif key == 27:
sys.exit()
pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
#보간법도 넣음
dst = cv2.warpPerspective(img, pers, (dw,dh), flags=cv2.INTER_CUBIC)
cv2.imshow('dst', dst)
cv2.waitKey()
'AI 컴퓨터 비전프로젝트' 카테고리의 다른 글
[DL] 모폴로지 처리, 도형이미지 구분하기 (1) | 2024.10.08 |
---|---|
[DL] 엣지검출(Canny) , 필터캠 (0) | 2024.10.07 |
[DL] 영상의 변환(이동), resize (0) | 2024.10.05 |
[DL] 적응형이진화(cv2.adaptiveThreshold())+자동이진화 적응형, 가우시안 비교 (0) | 2024.10.04 |
[DL] 오츠의 이진화(스레숄드값을 자동으로 잡아주기) (0) | 2024.10.03 |