[openCV] Image Geometric Transformation (1)
오늘은 openCV에서 이미지의 기하학적 변환에 대해서 정리를 해보려고 한다.
사실 제목에서도 알 수 있듯이 기하학적인 요소가 들어가 있어서
함수에 대해서 심화적인 이해를 하기 위해서는
수학에 대한 조금의 선수지식이 필요하다.
수학 어려워
0. 행렬의 필요성
github.com/ndb796/Python-Data-Analysis-and-Image-Processing-Tutorial.git
ndb796/Python-Data-Analysis-and-Image-Processing-Tutorial
파이썬을 활용한 데이터 분석과 이미지 처리 - 강의 자료 및 소스코드 Repository입니다. - ndb796/Python-Data-Analysis-and-Image-Processing-Tutorial
github.com
행렬의 필요성을 읽어보자
깔끔하게 정리를 잘 해놓으셨다👻
1. Rotation
이미지를 회전할 때 회전 행렬을 사용하게 된다.
회전횡렬이란?
오른쪽은 회전 후 좌표가 되는 값들이다.
[x' (회전된 x좌표), y' (회전된 y좌표), 1 (2차원 평면이므로 1)]
회전된 좌표를 구하는 방법은 왼쪽의 vecter와 가운데의 행렬을 곱해주면 된다.
vector의 방향이 세로이므로 행렬은 그에 대비되는 방향으로 곱해주자.
그러면 이런식으로 최종 식이 나온다.
x' (회전된 x좌표) = xCosθ - ySinθ
y' (회전된 y좌표) = xSinθ + yCosθ
이걸 실제로 2차원 평면에 공식을 유도해보면
이런식으로 나오게 된다.
www.youtube.com/watch?v=OVPyMijFiEQ&ab_channel=%ED%8C%8C%EA%B9%A8%EB%B9%84TV
참고하면 그뤠잇😦
openCV에서는 getRotationMatrix2D 함수를 사용하여서 위와 같은 회전 행렬을 생성하고
warpAffine 함수를 사용하여서 이미지에 회전 변환을 적용한다.
getRotationMatrix2D 함수는 배율 및 회전 중심 좌표가 추가된 회전 행렬을 사용한다.
코드를 살펴보자
import cv2
import imutils
img_color = cv2.imread("./cat.jpg")
img_color = imutils.resize(img_color, width=500, height=500)
cv2.imshow("Cuttyyyyyyy", img_color)
height, width = img_color.shape[:2]
M = cv2.getRotationMatrix2D((width / 2.0, height / 2.0), 45, 1)
print(type(height), width)
img_rotated = cv2.warpAffine(img_color, M, (width, height))
cv2.circle(img_rotated, (int(height / 2.0) , int(width / 2.0)), 3, (0, 255, 0), -1)
cv2.imshow("rotation", img_rotated)
cv2.waitKey(0)
cv2.destroyAllWindows()
하나하나 뜯어서 보자
height, width = img_color.shape[:2]
M = cv2.getRotationMatrix2D((width / 2.0, height / 2.0), 45, 1)
cv2.getRotationMatrix2D( CenterPosition, Rotation Angle, Magnification Of Image (scale) )
CenterPosition = 회전 할 때 중심
Rotation Angle = 회전할 각도Magnification Of Image = 이미지 배율 ( 1이면 이미지의 원래 크기 )
위의 Argument를 참고해보면
image의 중심이 회전의 중심점이 되며, 45도 만큼 회전하고, 이미지의 원래 크기 만큼
출력한다는 정보가 들어가있는 회전 행렬을 만들게 된다.
img_rotated = cv2.warpAffine(img_color, M, (width, height))
cv2.warpAffine( src, M, dsize )
src = 회전행렬을 적용할 Image
M = cv2.getRotationMatrix2D를 활용하여 생성된 변환 행렬
dsize = Image 출력 사이즈
[ Result ]
2. Scaling
S(x, y) : 사용자 지정 이미지 변환 값
x' = x * S(x)
y' = y * S(y)
코드를 살펴보기 전에 Interpolation (보간법)을 알아보자
openCV에서 Image를 Resize하는데 6가지의 Interpolation이 쓰인다
Interpolation은 픽셀로 이루어진 이미지를 사이즈를 키울 때 어떤 픽셀을 넣을지,
크기를 줄일 경우에는 손실되는 픽셀을 어떻게 선정하는지에 대한 방법이다.
이미지를 확대할 때 많이 사용하는 Interpolation
cv2.INTER_CUBIC : 바이큐빅 보간법
cv2.INTER_LINEAR : 쌍 선형 보간법
이미지를 축소할 때 많이 사용하는 interpolation
cv2.INTER_AREA : 영역 보간법
사실 Interpolation은 저것 외에도 굉장히 종류가 많아서
나중에 한번 정리를 해볼려고 계획하고 있는 중이다
이제 코드를 살...펴보자..
import cv2
import imutils
img_color = cv2.imread('cat.jpg')
img_color = imutils.resize(img_color, width=500, height=500)
cv2.imshow("Cuttyyyy", img_color)
img_result = cv2.resize(img_color, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
cv2.imshow("x2 INTER_CUBIC", img_result)
height, width = img_color.shape[:2]
img_result = cv2.resize(img_color, (3 * width, 3 * height), interpolation=cv2.INTER_LINEAR)
cv2.imshow("x3 INTER_LINEAR", img_result)
img_result = cv2.resize(img_color, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
cv2.imshow('x0.5 INTER_AREA', img_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.resize( img, dsize, fx, fy, interpolation )
img = 확대, 축소할 이미지
dsize = 이미지를 확대, 축소를 할 width, height 값 지정
fx, fy = 만약 dsize가 None이라면 원본 이미지의 상대적인 크기 지정 가능
interpolation = 보간법 선택
img_result = cv2.resize(img_color, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
cv2.imshow("x2 INTER_CUBIC", img_result)
현재 disize가 None이므로 fx, fy에 2를 넣어서 원본 이미지의 2배로 만들어줬다.
원본 이미지에 2배가 되는 것은 확대를 의미하므로 interpolation은 cv2.INTER_CUBIC을 지정했다.
img_result = cv2.resize(img_color, (3 * width, 3 * height), interpolation=cv2.INTER_LINEAR)
cv2.imshow("x3 INTER_LINEAR", img_result)
이건 위의 코드와 다르게 dsize를 지정해준것이다.
원본 이미지에 width, height에 3을 곱해주므로 3배가 된다.
interpolation은 cv.INTER_LINEAR로 지정한다.
img_result = cv2.resize(img_color, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
cv2.imshow('x0.5 INTER_AREA', img_result)
이건 원본 이미지를 축소하는 코드이다.
interpolation은 cv2.INTER_AREA로 지정했다.
[ Result ]
x3배는 너무 커서 화면에 안들어오지만 직접 해보면 차이를 알 것이다.
사실 난 cv2.resize()는 잘 안쓴다.
왜냐하면 Argument 자체가 너무 복잡하고
우리에겐 너무나 편한 imutils.resize()가 존재하기 때문이다.
3. Translation
이동 행렬은 이렇게 정의할 수 있습니다
T(x,y) = 사용자 지정 Postion
x' = x + T(x)
y' = y + T(y)
바로 코드로 넘어가보자
import cv2
import numpy as np
import imutils
img_color = cv2.imread('cat.jpg')
img_color = imutils.resize(img_color, width=500, height=500)
cv2.imshow("Cuttyyy", img_color)
height, width = img_color.shape[:2]
M = np.float32([[1, 0 ,100], [0, 1, 50]])
img_translation = cv2.warpAffine(img_color, M, (width, height))
cv2.imshow("translation", img_translation)
cv2.waitKey(0)
cv2.destroyAllWindows()
하나 하나 보자
M = np.float32([[1, 0 ,100], [0, 1, 50]])
numpy 배열을 활용해서 변환행렬을 만들어 주었다.
M = [1, 0, 100 = T(x)]
[0, 1, 50 = T(y)]
굳이 위 사진과 같이 표현을 해보자면 이런 식이 되는거다.
그럼 x' = x + 100이 되며
y' = y + 50이 되는 것이다.
그럼 x 좌표로 +100 이동하며
y 좌표로 +50 이동할 수 있다.
img_translation = cv2.warpAffine(img_color, M, (width, height))
이건 위에서도 했으니 큰 설명은 하지 않겠다.
[ Result ]
오늘은 내용이 너무 길어져 다음에 이어서 정리를 해야겠다ㅏ🙄
수학 열심히 해야겠다...
오늘도 끄-읕😶