728x90
반응형
영상의 기하학적 변환
- 영상의 밝기, 명암비 조절, 필터링 등은 픽셀 위치는 고정한 상태에서 픽셀 값만 변경했지만, 기하학적 변환이라는 것은 픽셀 값은 그대로 유지하면서 위치를 변경하는 작업
- 영상을 구성하는 픽셀의 배치 구조를 변경함으로 전체 영상의 모양을 바꾸는 작업
✔ 어파인 변환(affine transformation)
영상을 평행 이동시키거나 회전, 크기 변환 등을 통해 만들 수 있는 변환을 통칭
영상에서 (x,y) 좌표의 픽셀을 결과 영상의 (x' , y') 좌표로 변환하는 방법
{ x' = f1(x, y), y' = f2(x, y) }
{ x' = f1(x, y) = ax + by + c, , y' = f2(x, y) = dx + ey + f }
위 수식을 행렬을 이용하여 아래와 같이 표현할 수 있음
[[x'], [y']] = [[a, b], [d, e]] * [[x], [y]] + [[c], [f]]
위 수식을 행렬을 이용하여 아래와 같이 용약할 수 있음
[[x'], [y']] = [[a, b, c], [d, e, f]] * [[x], [y], [1]]
--------- --------
# 2 by 3 행렬로(점 3개) 각 꼭지점의 위치를 유추할 수 있다.
> 여섯개의 파라미터로 구성된 2*3 행렬 [[a, b, c], [d, e, f]]를 어파인 변환 행렬이라고 부름
어파인 변환 행렬을 계산
cv2.getAffineTransform(영상, 세 점의 좌표)
어파인 변환 행렬을 가지고 있을 때 영상을 어파인 변환한 결과 영상 생성
cv2.warpAffine(영상, 어파인 변환 행렬, 결과 영상 크기)
✔ 어파인 예제
# 어파인 변환 예제
import cv2
import numpy as np
src = cv2.imread('dog.jpg')
# 어파인 행렬을 일단 만들어 보자 200(x축), 100(y축) 만큼 이동할 것이다
# [1,0,tx],[0,1,ty]
# 음수 tx값은 이미지를 왼쪽으로 이동, 양수 tx값은 이미지를 오른쪽으로 이동
# 음수 ty값은 이미지를 위으로 이동, 양수 ty값은 이미지를 아래로 이동
aff = np.array([[1,0,200],[0,1,100]], dtype=np.float32)
#이미 어파인 행렬이 있기 때문에 aff를 넣어주었다.
dst = cv2.warpAffine(src, aff, (0,0))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
✔ 크기 변환(Scale)
영상의 크기를 원본 영상보다 크게 또는 작게 만드는 변환
cv2.resize(영상, 결과 영상 크기, x와 y방향 스케일 비율, 보간법)
✔ 보간법 : 기존에 알고 있는 특정 지점이나 지역의 속성값을 이용하여 알고자 하는 지점 또는 지역의 속성값을 찾아내는 방법이다
cv2.INTER_NEAREST : 최근방 이웃 보간법. 속도가 가장 빠르지만 퀄리티가 많이 떨어짐.
openCV 문서에서는 다운 샘플링일 경우 cv2.INTER_NEAREST를 사용하기 권장하고 있음
cv2.INTER_LINEAR : 양선형 보간법. 4새 픽셀을 이용하며 효율성이 가장 좋음
cv2.INTER_CUBIC : 3차 회선 보간법. 16개의 픽셀을 이용. INTER_LINEAR 보다 느리지만 퀄리티는 가장 좋음
cv2.INTER_AREA : 영상 다운 샘플링시 효과적. 영역적인 정보를 추출해서 결과 영상을 세팅
✔ 크기변환 예제
import cv2
src = cv2.imread('rose.bmp')
dst1 = cv2.resize(src, (0, 0), fx=4, fy=4,interpolation=cv2.INTER_NEAREST)
dst2 = cv2.resize(src, (1920, 1280))
dst3 = cv2.resize(src, (1920, 1280), interpolation=cv2.INTER_CUBIC)
cv2.imshow('src', src)
cv2.imshow('dst1', dst1[500:900, 400:800]) # 처리량은 빠르지만 질이 좋지 않다
cv2.imshow('dst2', dst2[500:900, 400:800]) # 기본값 : INTER_LINEAR
cv2.imshow('dst3', dst3[500:900, 400:800])
# 큐빅 : 사람눈에 큰 차이는 없지만 계산은 복잡하고 더 좋은 퀄리티를 보장한다.
cv2.waitKey()
✔ 회전
import cv2
src = cv2.imread('dog.jpg')
cp = (src.shape[1] / 2, src.shape[0] / 2) # 센터값(중심 좌표) 구하기
rot = cv2.getRotationMatrix2D(cp, 20, 0.5)
print(rot)
dst = cv2.warpAffine(src, rot, (0,0))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
✔ 영상의 회전(rotation)
- 영상을 특정 각도만큼 회전시키는 변환(반시계 방향)
cv2.getRotationMatrix2D(중심 좌표, 회전 각도, 확대 비율) -> affine 행렬값이 나온다.
회전 각도: 반시계 방향. 음수는 시계방향
✔ 투시변환 예제 및 설명들
✔ 투시 변환 : 4개의 꼭지점을 모두 알아야한다.
- 직사각형 형태의 영상을 임의의 블록 사각형 형태로 변경할 수 있는 변환
- 원본 영상에 있던 직선은 결과 영상에서 그대로 유지되지 않고 평행 관계가 깨질 수 있음
- 투시 변환은 보통 3*3 크기의 실수 행렬러 표현(8개의 파라미터로 표현할 수 있지만, 좌표 계산의 편의상 9개의 원소룰 갖는 행렬을 사용)
# 평행하게 나올지 꺽인 상태에서 나올지에 따라 2*3, 3*3인지만 알 수 있으면 된다.
점 4개의 이동 전, 이동 후 좌표를 입력하면 투시 변환 행렬을 반환
cv2.getPerspectiveTransform(영상, 4개의 결과 좌표점)
3*3 투시 변환 행렬을 가지고 있을 때 영상의 투시 변환 결과 영상을 생성
cv2.warpPerspective(영상, 투시 변환 행렬, 결과 영상 크기)
✔ 예제 1
import numpy as np
import cv2
src = cv2.imread('pic.jpg')
w, h = 600, 400
srcQuad = np.array([[370, 173],[1223,157],[1422,844],[211,865]], np.float32)
dstQuad = np.array([[0, 0],[w, 0],[w, h],[0, h]], np.float32)
pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
dst = cv2.warpPerspective(src, pers,(w,h))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
그림판을 사용하여 좌표를 구하였으며 비스듬한 사진을 평평하게 만들어주기
✔ 예제 2
import sys
import numpy as np
import cv2
def drawROI(img, corners):
cpy = img.copy() # 카피 이미지를 만든다.
c1 = (0, 0, 255) # 점 색상
c2 = (255, 0, 0) # 선 색상
for pt in corners: # 4바퀴 돌리기
# circle : 원을 만들자
cv2.circle(cpy, tuple(pt.astype(int)), 25, c1, -1, cv2.LINE_AA)
# 25(반지름), c1 점 색상, -1 배경을 지우고 , cv2.LINE_AA 선 타입(직선)
# line : 라인을 만들자
cv2.line(cpy, tuple(corners[0].astype(int)), tuple(corners[1].astype(int)), c2, 2, cv2.LINE_AA)
cv2.line(cpy, tuple(corners[1].astype(int)), tuple(corners[2].astype(int)), c2, 2, cv2.LINE_AA)
cv2.line(cpy, tuple(corners[2].astype(int)), tuple(corners[3].astype(int)), c2, 2, cv2.LINE_AA)
cv2.line(cpy, tuple(corners[3].astype(int)), tuple(corners[0].astype(int)), c2, 2, cv2.LINE_AA)
return cpy
# 이벤트, 좌표들, 클릭중인지, 데이터가 있으면 넘길것이 있는지
def onMouse(event, x, y, flags, param):
global srcQuad, dragSrc, ptOld, src # 글로벌로 설정을 하영 중복을 배제한다.
if event == cv2.EVENT_LBUTTONDOWN:
# LBUTTONDOWN : 왼쪽 버튼이 눌리고 있냐??
for i in range(4):
# 4개의 좌표가 있기 때문에 4번을 반복한다.
if cv2.norm(srcQuad[i] - (x, y)) < 25:
# 원의 반지름 안에 들어왔냐??
# norm 함수 : 정규화 시키는 함수이다.
dragSrc[i] = True
# 반지름 안에 들어왔으면
ptOld = (x, y)
break
if event == cv2.EVENT_LBUTTONUP: # 마우스 버튼이 떨어젔냐??
for i in range(4):
dragSrc[i] = False
# 떨어지니깐 False : 초기화 시키기 위해 사용하는 것이다.
if event == cv2.EVENT_MOUSEMOVE: # 마우스를 드레그 한다면??
for i in range(4):
if dragSrc[i]: # 드레그 중이라면 (True)
dx = x - ptOld[0] # 0번에 해당하는 x좌표는
dy = y - ptOld[1] # 1번이라는 y좌표
srcQuad[i] += (dx, dy) # 더해서 해당 좌표를 만든다.
cpy = drawROI(src, srcQuad) # 다시 그려라
cv2.imshow('img', cpy) # 다시 화면을 보여주기
ptOld = (x, y) # 그 값들을 다시 적용하자
break
# 입력 이미지 불러오기
src = cv2.imread('namecard.jpg')
h, w = src.shape[:2]
dh = 500
# A4용지 크기 : 219*297cm
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] # 지금 현재 점이 드래그 중인지 움직이면 True로 변경
# 모서리점, 사각형 그리기(얻어내는 함수를 의미한다)
disp = drawROI(src, srcQuad) # 원본에다가 드래그할 위치를 넘겨준다.
cv2.imshow('img', disp)
cv2.setMouseCallback('img', onMouse) # 마우스 이벤트
while True:
key = cv2.waitKey()
if key == 13: # ENTER 키
break
elif key == 27: # ESC 키
sys.exit()
if src is None:
print('Image open failed!')
sys.exit()
cv2.waitKey()
# 투시 변환
pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
dst = cv2.warpPerspective(src, pers, (dw, dh), flags=cv2.INTER_CUBIC) # 확대 시킬거기 때문에 CUBIC을 사용
cv2.imshow('dst', dst)
cv2.waitKey()
비스듬한 사진을 마우스 이벤트로 예제1과 동일하게 평평하게 만들기
반응형
'Python > openCV' 카테고리의 다른 글
테서렉트 예제 (0) | 2023.03.17 |
---|---|
테서렉트 설치(사전준비) (0) | 2023.03.16 |
Python OpenCV (영상 필터링, 블러, 가우시안, 미디안, 샤프닝, 케니) (0) | 2023.03.12 |
Python OpenCV (영상의 이진화, 자동 이진화, 지역 이진화, 적응형 이진화, 모폴로지) (0) | 2023.03.10 |
Python openCV 기본 (0) | 2023.03.08 |