Latte는 수능에 무조건 기하와벡터가 출제되서 수학의 정석으로 공부를 했었었었던 기억이 있는데, 요즘은 기벡이 선택과목인 것도 모자라 앞으로 안나올 수도 있다니...기벡 참 재미있었는데, 그 재미를 알게 될 기회조차 없는 요즘 고등학생들이 안타깝다
사실 재미있었다고만 했지 잘했다고는 한적이 없다ㅋㅋ;
앞으로 배울 컴퓨터그래픽스와 컴퓨터비전 과목에 기하학적 변환 기법이 많이 사용될 것 같으니 많이 연습해 놓으면 분명 도움이 될 것 같다.
: 가로 또는 세로 방향으로 영상을 특정 크기만큼 이동시키는 변환
cv2.warpAffine(src, M, dsize, dst=None, flags=None, orderMode=None, borderValue=None) -> dst
1) src : 입력 영상
2) M : 2x3 어파인 변환 행렬 (실수형)
3) dsize : 결과 영상 크기 (w,h) 튜플, (0,0)이면 src와 같은 크기로 설정
4) dst : 출력 영상
5) flags : 보간법, 기본값은 cv2.INTER_LINEAR
6) borderMode : 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_CONSTANT
7) borderValue : cv2.BORDER_CONSTANT일 때 사용할 상수값. 기본값은 0
import sys
import numpy as np
import cv2
src = cv2.imread('tekapo.bmp')
if src is None:
print('Image load failed!')
sys.exit()
# Affine 변환 행렬
aff = np.array([[1, 0, 200],
[0, 1, 100]], dtype=np.float32)
dst = cv2.warpAffine(src, aff, (0, 0))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
결과를 보면 이미지가 창에서 우측 하단으로 affine 변환행렬에 의해 이동한 것을 알 수 있다.
: 층 밀림 변환. x축과 y축의 방향에 대해 따로 정의한다.
import sys
import numpy as np
import cv2
src = cv2.imread('tekapo.bmp')
if src is None:
print('Image load failed!')
sys.exit()
aff = np.array([[1, 0.5, 0],
[0, 1, 0]], dtype=np.float32)
h, w = src.shape[:2]
dst = cv2.warpAffine(src, aff, (w + int(h * 1.5), h))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
여기까지가 영상의 위치나 모양을 변환시키는 작업이고, 지금부터는 크기를 변환시키는 작업이다.
cv2.resize(src, dsize, fx=None, fy=None, interpolation=None) -> dst
1) src : 입력 영상
2) dsize : 결과 영상 크기 (w,h) 튜플. (0,0) 이면 fx와 fy 값을 이용하여 결정
3) dst : 출력 영상
4) fx, fy : x와 y방향 스케일 비율 (dsize 값이 0일 때 유효)
5) interpolation : 보간법 지정. 기본값은 cv2.INTER_LINEAR
-> n x n 행렬에서 n이 커질수록 정확도가 높아지지만, 연산량의 증가율이 훨씬 더 크기 때문에 시간도 오래걸리게 된다.
import sys
import numpy as np
import cv2
src = cv2.imread('rose.bmp') # src.shape=(320, 480)
if src is None:
print('Image load failed!')
sys.exit()
dst1 = cv2.resize(src, (0, 0), fx=4, fy=4, interpolation=cv2.INTER_NEAREST)
dst2 = cv2.resize(src, (1920, 1280)) # cv2.INTER_LINEAR
dst3 = cv2.resize(src, (1920, 1280), interpolation=cv2.INTER_CUBIC)
dst4 = cv2.resize(src, (1920, 1280), interpolation=cv2.INTER_LANCZOS4)
cv2.imshow('src', src)
cv2.imshow('dst1', dst1[500:900, 400:800])
cv2.imshow('dst2', dst2[500:900, 400:800])
cv2.imshow('dst3', dst3[500:900, 400:800])
cv2.imshow('dst4', dst4[500:900, 400:800])
cv2.waitKey()
cv2.destroyAllWindows()
cv2.flip(src, flipCode, dst=None) -> dst
1) src : 입력 영상
2) flipcode : 대칭 방향 지정 [+1 : 좌우 대칭, 0 : 상하 대칭, -1 : 상하좌우 대칭]
3) dst : 출력 영상
: 하나의 영상에 대해 다양한 해상도의 영상 세트를 구성한 것
cv2.pyrDown(src, dst=None, dstsize=None, borderType=None) -> dst
1) src : 입력 영상
2) dst : 출력 영상
3) dstsize : 출력 영상 크기 - 따로 지정하지 않으면 입력 영상의 가로, 세로 크기의 1/2로 설정
4) borderType : 가장 자리 픽셀 확장 방식
5) 참고 사항
- 먼저 5x5 크기의 가우시안 필터를 적용
- 이후 짝수 행과 열을 제거하여 작은 크기의 영상을 생성
cv2.pyrup(src, dst=None, dstsize=None, borderType=None) -> dst
1) src : 입력 영상
2) dst : 출력 영상
3) dstsize : 출력 영상 크기 - 따로 지정하지 않으면 입력 영상의 가로, 세로 크기의 2배로 설정
4) borderType : 가장 자리 픽셀 확장 방식
import sys
import numpy as np
import cv2
src = cv2.imread('cat.bmp')
if src is None:
print('Image load failed!')
sys.exit()
rc = (250, 120, 200, 200) # rectangle tuple
# (250, 120) 에서 시작, 가로 & 세로 길이= 200
# 원본 영상에 그리기
cpy = src.copy()
cv2.rectangle(cpy, rc, (0, 0, 255), 2)
cv2.imshow('src', cpy)
cv2.waitKey()
# 피라미드 영상에 그리기
for i in range(1, 4):
src = cv2.pyrDown(src) # 다운샘플링
cpy = src.copy()
cv2.rectangle(cpy, rc, (0, 0, 255), 2, shift=i)
cv2.imshow('src', cpy)
cv2.waitKey()
cv2.destroyWindow('src')
cv2.destroyAllWindows()
: 영상 관련해서는 회전을 빼놓을 수가 없다. 선형대수에서 배운 회전 행렬을 이용해서 픽셀값을 회전시키면 영상 전체가 회전되는 원리를 이용한다.
math 라이브러리에 있는 math.sin과 math.cos 함수를 이용하면 회전행렬을 쉽게 구할 수 있다.
import sys
import math
import numpy as np
import cv2
src = cv2.imread('tekapo.bmp')
if src is None:
print('Image load failed!')
sys.exit()
# 라디안 값 구하기
rad = 20 * math.pi / 180
# Affine 행렬 (회전 행렬) 구하기
aff = np.array([[math.cos(rad), math.sin(rad), 0],
[-math.sin(rad), math.cos(rad), 0]], dtype=np.float32)
dst = cv2.warpAffine(src, aff, (0, 0))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
이 코드는 전단변환을 하는 작업을 회전행렬로 해서 영상이 전체적으로 radian 만큼 반시계 방향으로 회전되어 있는 것을 알 수 있다.
cv2.getRotationMatrix2D(center, angle, scale) -> retval
1) center : 회전 중심 좌표 (x,y) 튜플
2) angle : (반시계 방향) 회전 각도 (degree). 음수는 시계 방향
3) scale : 추가적인 확대 비율
4) retval : 2x3 어파인 변환 행렬. 실수형
src=cv2.imread('tekapo.bmp')
cp=(src.shape[1]/2, src.shape[0]/2)
rot=cv2.getRotationMatrix2D(cp, 20, 1)
dst=cv2.warpAffine(src, rot, (0,0))
이런식으로 코드를 바꾸면 똑같이 회전을 하더라도 이미지의 중심을 축으로 두고 회전을 하게 되기 때문에 잘리는 부분이 적어지게 된다.
cv2.getAffineTransform(src, dst) -> retval
1) src : 3개의 원본 좌표점. numpy.ndarray.shape=(3,2)
e.g) np.array([[x_1, y_1], [x_2, y_2], [x_3, y_3]], np.float32)
2) dst : 3개의 결과 좌표점. numpy.ndarray.shape=(3,2)
3) retval : 2x3 투시 변환 행렬
cv2.getPerspectiveTransform(src, dst, solveMethod=None)
-> retval
1) src : 4개의 원본 좌표점. numpy.ndarray.shape=(4,2)
e.g) np.array([[x_1, y_1], [x_2, y_2], [x_3, y_3], [x_4, y_4]], np.float32)
2) dst : 4개의 결과 좌표점. numpy.ndarray.shape=(4,2)
3) retval : 3x3 투시 변환 행렬
cv2.warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None) -> dst
1) src : 입력 영상
2) M : 2x3 어파인 변환 행렬. 실수형
3) dsize : 결과 영상 크기. (w,h) 튜플. (0,0)이면 src와 같은 크기로 설정
4) dst : 출력 영상
5) flags : 보간법. 기본값은 cv2.INTER_LINEAR
6) borderMode : 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_CONSTANT
7) borderValue : cv2.BORDER_CONSTANT일 때 사용할 상수 값. 기본 값은 0
cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None) -> dst
1) src : 입력 영상
2) M : 3x3 투시 변환 행렬. 실수형
3) dsize : 결과 영상 크기. (w,h) 튜플. (0,0)이면 src와 같은 크기로 설정
4) dst : 출력 영상
5) flags : 보간법. 기본값은 cv2.INTER_LINEAR
6) borderMode : 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_CONSTANT
7) borderValue : cv2.BORDER_CONSTANT일 때 사용할 상수 값. 기본 값은 0
import sys
import numpy as np
import cv2
src = cv2.imread('namecard.jpg')
if src is None:
print('Image load failed!')
sys.exit()
w, h = 720, 400
# 기울어져 있는 명함의 edge 좌표
srcQuad = np.array([[325, 307], [760, 369], [718, 611], [231, 515]], np.float32)
# 펴진 명함의 edge 좌표
dstQuad = np.array([[0, 0], [w-1, 0], [w-1, h-1], [0, h-1]], np.float32)
pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
dst = cv2.warpPerspective(src, pers, (w, h))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
: 영상의 특정 위치 픽셀을 다른 위치에 재배치하는 일반적인 프로세스
cv2.remap(src, map1, map2, interpolation, dst=None, borderMode=None, borderValue=None) -> dst
1) src : 입력 영상
2) map1 : 결과 영상의 (x,y) 좌표가 참조할 입력 영상의 x좌표
입력 영상과 크기는 같고, 타입은 np.float32인 numpy.ndarray
3) map2 : 결과 영상의 (x,y) 좌표가 참조할 입력 영상의 y좌표
4) interpolation : 보간법
5) dst : 출력 영상
6) borderMode : 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_CONSTANT
7) borderValue : cv2.BORDER_CONSTANT일 때 사용할 상수 값. 기본 값은 0