4개의 좌표가 있다고 가정하자. 이것을 8개로 늘리고 싶다면 의 값은 그대로지만 나머지 값들은 아닐 것이다. 이를 어떻게 처리해야할까? Interpolation 을 해야한다.
Interpolation 이란 이미지를 확대하거나 축소할 때, 원래의 픽셀 값을 새 위치에 맞추어 새 픽셀 값을 계산하는 과정이다. 간단히 말해, 원래의 픽셀들이 없는 위치에 값을 채워 넣는 기술로, 이미지의 품질을 유지하면서 크기를 조정할 수 있게 해준다.
가장 간단한 interpolation 방법이다. 검은 점은 원래 점 , 흰 점은 interpolation 결과인 점들이다. 은 가장 가까운 의 값으로 결정한다. 즉,
,
,
,
,
,
,
,
,
이다.
좌우에 이웃한 두 점 사이의 거리 ratio 를 계산해서 를 결정한다. Linear function 그래프를 그린다고 생각하면 편하다.
2D array 에서 linear interpolation 을 한다고 생각하면 된다. 주변 4개의 점에 가중치를 계산해서 를 결정한다.
Ans: 5
실제 이미지 파일에서 어떻게 interpolation 이 작동되는지를 이해하는 것이 중요하다. 위의 문제를 직접 풀어보면서 2D array 에서의 interpolation 과정을 이해해보자.
가중치를 계산하는 weight function, 를 사용해서 를 결정한다. 결과물이 가장 선명하지만 계산량 또한 가장 많다. 컴퓨터의 연산량이 많아 속도가 느리므로 실습에서는 선호하지 않는다.
위 수식처럼 nearest neighbor interpolation, linear interpolation 도 bicubic interpolation 의 방법으로 표현할 수 있다.
skimage.transform.rescale(image, scale, order=1)
import numpy as np
import matplotlib.pyplot as plt
import skimage.transform as tr
camera_man = np.array(Image.open('cameraman.tif')
camera_man_head = camera_man[32:96, 89:153]
# Nearest neighbor interpolation
head1 = tr.rescale(camera_man_head, 2, order=0)
# Bilinear: returns pixel values [0.0 1.0]
head2 = tr.rescale(camera_man_head, 2, order=1) * 255
# Bicubic: returns pixel values [0.0 1.0]
head3 = tr.rescale(camera_man_head, 2, order=3) * 255
plt.figure()
plt.subplot(2, 2, 1)
plt.imshow(cameraman_head_np, cmap='gray')
plt.axis('off')
plt.title('Original head')
plt.subplot(2, 2, 2)
plt.imshow(head1, cmap='gray')
plt.axis('off')
plt.title('Nearest neighbor interpolation')
plt.subplot(2, 2, 3)
plt.imshow(head2, cmap='gray')
plt.axis('off')
plt.title('Linear interpolation')
plt.subplot(2, 2, 4)
plt.imshow(head3, cmap='gray')
plt.axis('off')
plt.title('Bicubic interpolation')
plt.show()
이미지를 그저 2배 키우기 위한 목적이라면 컨벌루션을 이요해 빠르게 확대할 수 있다. 말 그대로 크기를 두 배로 키워서 빈 공간에는 0으로 채워 넣는다. 원본 이미지의 두 배 크기의 np.zeros 를 만든 후, i, j 좌표 모두가 홀수인 곳에만 원래 픽셀 값을 채워넣는다.
Zero-padded np array
import numpy as np
import scipy.ndimage as ndi
# m: Input image
m = np.array([[16, 2, 3, 13],
[5, 11, 10, 8],
[9, 7, 6, 12],
[4, 14, 15, 1]])
height, width = m.shape
# Input image 의 2배 사이즈의 np.zeros 생성
m2 = np.zeros((2 * hegith, 2 * width))
# np.zero 에서 i, j 좌표가 모두 홀수인 곳에만 input image의 pixel value 입력
m2[::2, ::2] = m
그 후 아래의 필터 마스크를 convolution 하면 interpolation 결과물을 얻을 수 있다.
mm1 = ndi.convolve(m2, np.array([[1, 1, 0], [1, 1, 0], [0, 0, 0]]), mode='constant')
mm2 = ndi.convolve(m2, np.array([[1, 2, 1], [2, 4, 2], [1, 2, 2]])/4.0, mode='constant')
print(mm1); print(mm2)
이미지를 함부로 축소시키면 t2처럼 손상된 부분이 생길 수 있다. Bicubic interpolation 로 축소하면 이런 문제를 방지할 수 있다. 미리 LPF 로 필터링하는 것도 좋은 방법이다.
특정 각도로 이미지를 회전하고 싶으면 아래의 공식을 사용하면 된다.
직접 행렬 식을 계산해보면 왜 이런 공식이 도출되었는지 알 수 있을 것이다. 문제는 이 계산의 결과 값이 정수가 아닐 수 있다는 것이다. 또한 회전으로 인해 grid 의 크기가 더 커질 수 있다(이미지는 numpy 배열로 구현되고, 항상 직사각형 모양이기에 grid는 몇몇 특수한 상황을 제외하면 필연적으로 커진다). 아래의 구현 방법을 보면 이 문제점에 대해 더 쉽게 이해할 수 있을 것이다.
skimage.transform.rotate(image, angle, resize=False, center=None, order=1, mode='constant', cval=0, clip=Ture, preserve_range=False)
import skimage.transform as tr
# 시계 반대 방향으로 60도 회전, nearest neighbor interpolation
# grid 에 image 가 모두 들어오도록 크기를 resize
cr = tr.rotate(c, 60, order=0, resize=True)
# 시계 반대 방향으로 60도 회전, bicubic interpolation
# 이미지의 크기를 유지
crc = tr.rotate(c, 60, order=3)
플로팅 된 화면에서는 두 결과 이미지의 크기가 같아 보이지만, 실제로는 cr의 크기가 더 크다(화질을 비교하라). crc는 회전 후 원본 이미지와 pixel size를 유지하기 위해 crop한 것이기 때문에 pixel size는 cr이 더 클 수밖에 없다.
2D 또는 3D 공간에서 점, 직선 등의 기하학적 객체를 선형 변환과 평행 이동으로 변형하는 방법이다. 이 변환은 직선성을 보존하기 때문에 직선이 곡선으로 바뀌지 않고 평행한 직선은 변환 후에도 평행을 유지한다.
주요 affine transform 에는 다음이 포함된다:

skimage.transform.AffineTransform(scale=None, rotation=None, shear=None, translation=None)
skimage.transform.warp(image, inverse_map, map_args=None, output_shape=None, order=None, mode=’constant’, cval=0.0, clip=True, preserve_range=False)
import skimage.transform as tr
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
image = np.array(Image.open('body.jpg'))
# 회전 각도를 라디안으로 변환
rot = 10 * (3.14159 / 180)
# Affine transformation 정의
tform1 = tr.AffineTransform(rotation=rot)
# 수평 방향으로 0.5 라디안 shear
shr = 0.5
tform2 = tr.AffineTransform(shear=shr, translation=(100, 0))
# 첫 번째 affine transform 실행 (rotation)
rotated_image = tr.warp(image, tform1)
# 두 번째 affine transform 실행 (shear)
sheared_image = tr.warp(image, tform2)
# 결과 이미지 시각화
fig, ax = plt.subplots(1, 3, figsize=(15, 5))
ax[0].imshow(image, cmap='gray')
ax[0].set_title("Original Image")
ax[0].axis('off')
ax[1].imshow(rotated_image, cmap='gray')
ax[1].set_title("Rotated Image")
ax[1].axis('off')
ax[2].imshow(sheared_image, cmap='gray')
ax[2].set_title("Sheared Image")
ax[2].axis('off')
plt.show()