영상의 이동 변환과 전단 변환
이동 변환 [Translation transformation]
: 가로 또는 세로 방향으로 영상을 특정 크기만큼 이동시키는 변환
cv2.warpAffine(src, M, dsize, dst=None, flags=None, orderMode=None, borderValue=None) -> dst
- src : 입력 영상
- M : 2x3 어파인 변환 행렬 [실수형]
- dsize : 결과 영상 크기 [w,h] 튜플, [0,0]이면 src와 같은 크기로 설정
- dst : 출력 영상
- flags : 보간법, 기본값은 cv2.INTER_LINEAR
- borderMode : 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_CONSTANT
- 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 변환행렬에 의해 이동한 것을 알 수 있다.
전단 변환 [Shear translation]
: 층 밀림 변환. 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()
여기까지가 영상의 위치나 모양을 변환시키는 작업이고, 지금부터는 크기를 변환시키는 작업이다.
영상의 확대와 축소
크기 변환 [Scale transformation]
- x축과 y축 방향으로의 스케일 비율을 지정한다.
영상의 크기 변환
cv2.resize(src, dsize, fx=None, fy=None, interpolation=None) -> dst
- src : 입력 영상
- dsize : 결과 영상 크기 [w,h] 튜플. [0,0] 이면 fx와 fy 값을 이용하여 결정
- dst : 출력 영상
- fx, fy : x와 y방향 스케일 비율 [dsize 값이 0일 때 유효]
- 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()
영상의 축소 시 고려할 사항
- 한 픽셀로 구성된 선분 같은 경우 영상 축소 시 디테일이 사라지는 경우가 발생한다
- 입력 영상을 부드럽게 필터링한 후 축소, 다단계 축소를 진행한다.
- OpenCV의 cv2.resize() 함수에서는 cv2.INTER_AREA 플래그를 사용한다.
영상의 대칭 변환
cv2.flip(src, flipCode, dst=None) -> dst
- src : 입력 영상
- flipcode : 대칭 방향 지정 [+1 : 좌우 대칭, 0 : 상하 대칭, -1 : 상하좌우 대칭]
- dst : 출력 영상
이미지 피라미드
: 하나의 영상에 대해 다양한 해상도의 영상 세트를 구성한 것
- 보통 가우시안 블러링 & 다운 샘플링 형태로 축소하여 구성한다.
영상 피라미드 다운샘플링
cv2.pyrDown(src, dst=None, dstsize=None, borderType=None) -> dst
- src : 입력 영상
- dst : 출력 영상
- dstsize : 출력 영상 크기 - 따로 지정하지 않으면 입력 영상의 가로, 세로 크기의 1/2로 설정
- borderType : 가장 자리 픽셀 확장 방식
- 참고 사항
- 먼저 5x5 크기의 가우시안 필터를 적용
- 이후 짝수 행과 열을 제거하여 작은 크기의 영상을 생성
영상 피라미드 업샘플링
cv2.pyrup(src, dst=None, dstsize=None, borderType=None) -> dst
- src : 입력 영상
- dst : 출력 영상
- dstsize : 출력 영상 크기 - 따로 지정하지 않으면 입력 영상의 가로, 세로 크기의 2배로 설정
- 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
- center : 회전 중심 좌표 [x,y] 튜플
- angle : [반시계 방향] 회전 각도 [degree]. 음수는 시계 방향
- scale : 추가적인 확대 비율
- 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
- 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] - dst : 3개의 결과 좌표점. numpy.ndarray.shape=[3,2]
- retval : 2x3 투시 변환 행렬
투시 변환 행렬
cv2.getPerspectiveTransform(src, dst, solveMethod=None)
-> retval
- 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] - dst : 4개의 결과 좌표점. numpy.ndarray.shape=(4,2)
- retval : 3x3 투시 변환 행렬
영상의 어파인 변환 함수
cv2.warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None) -> dst
- src : 입력 영상
- M : 2x3 어파인 변환 행렬. 실수형
- dsize : 결과 영상 크기. [w,h] 튜플. [0,0]이면 src와 같은 크기로 설정
- dst : 출력 영상
- flags : 보간법. 기본값은 cv2.INTER_LINEAR
- borderMode : 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_CONSTANT
- borderValue : cv2.BORDER_CONSTANT일 때 사용할 상수 값. 기본 값은 0
영상의 투시 변환 함수
cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None) -> dst
- src : 입력 영상
- M : 3x3 투시 변환 행렬. 실수형
- dsize : 결과 영상 크기. [w,h] 튜플. [0,0]이면 src와 같은 크기로 설정
- dst : 출력 영상
- flags : 보간법. 기본값은 cv2.INTER_LINEAR
- borderMode : 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_CONSTANT
- 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
- src : 입력 영상
- map1 : 결과 영상의 [x,y] 좌표가 참조할 입력 영상의 x좌표
입력 영상과 크기는 같고, 타입은 np.float32인 numpy.ndarray - map2 : 결과 영상의 [x,y] 좌표가 참조할 입력 영상의 y좌표
- interpolation : 보간법
- dst : 출력 영상
- borderMode : 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_CONSTANT
- borderValue : cv2.BORDER_CONSTANT일 때 사용할 상수 값. 기본 값은 0
- Chapter5 끝! -

'OpenCV' 카테고리의 다른 글
[OpenCV] 이진 영상 처리 (0) | 2025.04.04 |
---|---|
[OpenCV] 영상의 특징 추출 (0) | 2025.04.04 |
[OpenCV] 필터링 (1) | 2025.04.04 |
[OpenCV] 기본 영상 처리 (0) | 2025.04.04 |
[OpenCV] OpenCV 기초 (0) | 2025.04.04 |