소스 뷰어
어파인 변환의 연결¶
import numpy as np, math, cv2
def bilinear_value(img, pt):
x, y = np.int32(pt)
if y >= img.shape[0]-1: y = y - 1
if x >= img.shape[1]-1: x = x - 1
P1, P2, P3, P4 = np.float32(img[y:y+2,x:x+2].flatten())
alpha, beta = pt[1] - y, pt[0] - x # 거리 비율
M1 = P1 + alpha * (P3 - P1) # 1차 보간
M2 = P2 + alpha * (P4 - P2)
P = M1 + beta * (M2 - M1) # 2차 보간
return np.clip(P, 0, 255) # 화소값 saturation후 반환
def contain(p, shape): # 좌표(y,x)가 범위내 인지 검사
return 0<= p[0] < shape[0] and 0<= p[1] < shape[1]
def affine_transform(img, mat):
rows, cols = img.shape[:2]
inv_mat = cv2.invertAffineTransform(mat) # 어파인 변환의 역행렬
## 리스트 생성 방식
pts = [np.dot(inv_mat, (j, i, 1)) for i in range(rows) for j in range(cols)]
dst = [bilinear_value(img, p) if contain(p, size) else 0 for p in pts]
dst = np.reshape(dst, (rows, cols)).astype('uint8')
return dst
def getAffineMat(center, degree, fx = 1, fy = 1, translate = (0,0)):
cen_trans = np.eye(3, dtype=np.float32)
org_trans = np.eye(3, dtype=np.float32)
scale_mat = np.eye(3, dtype=np.float32) # 크기 변경 행렬
trans_mat = np.eye(3, dtype=np.float32) # 평행 이동 행렬
rot_mat = np.eye(3, dtype=np.float32) # 회전 변환 행렬
radian = (degree/180.0) * np.pi # 회전 각도 - 라디언 계산
rot_mat[0] = [ np.cos(radian), np.sin(radian), 0]
rot_mat[1] = [-np.sin(radian), np.cos(radian), 0]
cen_trans[:2, 2] = center # 중심 좌표를 기준으로 회전
org_trans[:2, 2] = np.multiply(center[0], -1) # 원점으로 이동
trans_mat[:2, 2] = translate # 평행 이동 행렬의 원소 지정
scale_mat[0, 0], scale_mat[1, 1] = fx, fy # 크기 변경 행렬의 원소 지정
ret_mat = cen_trans.dot(rot_mat.dot(trans_mat.dot(scale_mat.dot(org_trans))))
# ret_mat = cen_trans.dot(rot_mat.dot(scale_mat.dot(trans_mat.dot(org_trans))))
return np.delete(ret_mat, 2, axis=0) # 행 제거 ret_mat[0:2,:]
image = cv2.imread('img/affine.jpg', cv2.IMREAD_GRAYSCALE)
if image is None: raise Exception("영상 파일 읽기 에러")
size = image.shape[::-1]
center = np.divmod(size, 2)[0] # 회전 중심 좌표 - 크기는 행,열의 역순
angle, tr = 45.0, (200, 0) # 각도와 평행이동
aff_mat1 = getAffineMat(center, angle) # 중심 좌표 기준 회전
aff_mat2 = getAffineMat((0,0), 0, 2.0, 1.5) # 크기 변경 - 확대
aff_mat3 = getAffineMat(center, angle, 0.7, 0.7) # 회전 및 축소
aff_mat4 = getAffineMat(center, angle, 0.7, 0.7, tr) # 복합 변환
dst1 = cv2.warpAffine(image, aff_mat1, size) # OpenCV 함수
dst2 = cv2.warpAffine(image, aff_mat2, size)
dst3 = affine_transform(image, aff_mat3) # 사용자 정의 함수
dst4 = affine_transform(image, aff_mat4)
cv2.imshow("image", image)
cv2.imshow("dst1_only_rotate", dst1)
cv2.imshow("dst2_only_scaling", dst2)
cv2.imshow("dst3_rotate_scaling", dst3)
cv2.imshow("dst4_rotate_scaling_translate", dst4)
cv2.waitKey(0)
- 실행 결과에서 dst1은 중심점에서 45도의 회전만 수행하고, dst2는 크기변경만 수행한다. 그리고 dst3은 회전, 크기 변경을 적용했으며, dst4는 dst3에 x방향으로 200화소 평행이동을 한다. 여기서 dst4가 dst3에서 x 방향으로 평행이동을 수행하도록 했는데, 결과에서는 대각선 방향으로 평행이동이 된다, 이것은 st4에 회전 변환 후에 평행 이동이 적용되기 대문에 45도 기울어진 방향으로 평행이도 되기 대문이다. 즉 각 변환 행렬을 적용하여 하나의 어파인 행렬로 만들고, 이것을 영상에 적용할 경우에 변환 행렬을 곱하는 순서가 중요함을 보여주는 것이다.