[코드]

 

일반적으로 Image Super-Resolution (SR) 모델을 학습할 때는

 

많은 양의 high-resolution (HR) 이미지를 준비한 뒤 이를 임의의 downsampling 방법으로 줄여

 

input low-resolution (LR)을 만든다.

 

그 후 이렇게 만들어진 pair들을 통해 supervised learning을 진행한다.

 

해당 방법을 real-world application에 적용하기에는 많은 문제가 있으나,

 

아무튼 전통적으로 내려오던 framework이기 때문에

 

어느 모델을 개발하던 이러한 과정은 최소 한 번 이상 거치게 된다.

 

그런데 SR 관련 연구를 하며 가장 거슬렸던 부분 중 하나는 저 임의의 downsampling 부분이다.

 

 

딥러닝이 대세로 나서기 이전에는 MATLAB이 비전 분야의 암묵적인 standard였던 것 같다.

 

그런데 여기서 문제는, MATLAB에서 이미지의 크기를 바꾸는 데에 사용하는 imresize 함수가

 

OpenCV나 다른 이미지 관련 라이브러리들과는 상당히 다른 구현을 가지고 있다는 점이다.

 

특히 문제가 되는 것은 기본으로 적용되는 bicubic 보간법인데,

 

아무리 결과물을 비교하고 설정을 바꿔봐도 다른 라이브러리가 만드는 LR 이미지와는 상당한 차이가 있었다.

 

SR 분야의 벤치마크 이미지들은 거의 다 MATLAB으로 만들어졌기 때문에,

 

만약 다른 라이브러리로 LR 이미지들을 만들고 학습에 사용하는 경우

 

training-test distribution에 차이가 생겨 벤치마크 성능이 저하되고,

 

기껏 알고리즘을 잘 만들어놓고도 제대로 된 evaluation을 할 수가 없다.

 

그렇기 때문에 SR 연구에서는 오랫동안 이미지 resizing이 필요한 부분을 우선 MATLAB으로 모두 처리하고,

 

그 후 적절한 framework를 사용하여 추가적인 resizing 작업을 시행하지 않고 학습을 진행했다.

 

안타깝게도 MATLAB과 Python의 접착성은 별로 좋지 않기 때문에,

 

Python 코드가 도는 도중 다이내믹하게 MATLAB의 imresize를 호출해서 적절한 작업을 수행한다는 것은

 

시도할 염두를 내지도 못했고 이 때문에 작업 효율이 상당히 떨어진다는 느낌을 받았다.

 

 

이러한 문제를 좀 완화하고자 몇 사람이 numpy로 MATLAB-compatible 하다고 주장하는 imresize 함수를 구현했고

 

Github에서 어렵지 않게 찾아볼 수 있다.

 

그런데 실제로 확인해보니, 다양한 문제가 있었는데

  • 너무 느리다.
    numpy라서 기본적으로 GPU 연산을 지원하지 않을 뿐만 아니라, 최적화도 거의 되어있지 않다.
    imresize는 최소 수백, 수천번을 호출하는 함수인데
    이미지의 크기가 크면 눈에 띄게 성능이 저하되는 것을 느꼈다.
  • MATLAB-compatible하지 않다.
    공개된 코드를 직접 돌려서 확인해보니 MATLAB과 거의 비슷한 결과가 나왔지만,
    특히 boundary 부분에서 MATLAB과는 많이 달랐다.
    이유도 찾았는데, 이는 후술.
  • 미분 불가능.
    이는 큰 문제가 아니긴 한데 기본적으로 numpy로 구현되어 PyTorch layer 등에 넣을 수 없다.
    넣는 순간 computation graph가 깨져, PyTorch의 가장 큰 장점인 자동 미분을 사용할 수 없다.

개인적으로 저런 것들을 참고 쓰는 성격은 아니기 때문에,

 

작년에 (굉장히 늦은 타이밍이지만. 이렇게 정리 글을 쓰는 타이밍은 더 늦고.) 큰 맘먹고 직접 구현해봤다.

 

상당한 고생을 했지만 막상 만들고 나니 의외로 유용하게 쓰이기에, 잘 만들었다는 생각 또한 들었다.

 

 

본격적으로 구현 detail을 정리하기 전에,

 

이미지 resizing에 대한 기본 배경 지식을 정리해놓으면 나중에도 유용하게 참고할 수 있을 것 같다.

 

우선 좌표계를 정의하는 것이 정말 중요하다.

 

우리가 일반적으로 Python에서 $H \times W$ 이미지를 다룰 때는,

 

img[0, 0] (채널 생략)이 가장 왼쪽 상단, img[H - 1, W - 1]이 가장 오른쪽 하단의 픽셀을 의미한다.

 

수직, 수평 방향으로 이웃한 픽셀은 1만큼 떨어져 있기 때문에 이를 실제로 배치해보면 아래와 같다.

Python에서 이미지 픽셀의 레이아웃.

그런데 실제로 이미지를 이렇게 표현하면 상당히 난감한 것이, 이미지 (빨간 점선)의 크기가

 

$H \times W$가 아닌 $(H - 1) \times (W - 1)$이 나온다.

 

(딴소리지만, PyTorch의 interpolation 관련 함수에서 받는 align_corners 인수가 이러한 요인과 관련되어있다.

링크에서 조금 읽어보면 바로 느낌이 올 듯.)

 

따라서 편의상, 각 픽셀이 $1 \times 1$의 공간을 점유한다고 가정하면 (실제 픽셀의 정의와는 다르다!)

 

아래와 같이 이미지를 (-0.5, -0.5)와 (H - 0.5, W - 0.5) 사이의 영역에서 정의할 수 있다.

수정된 픽셀 레이아웃.

이렇게 정의한 이미지는 정상적으로 $H \times W$의 크기를 갖는다.

 

다른 방법도 다양하게 있겠지만, 이 방법이 가장 직관적인 것 같다.

 

사람에 따라서는 음의 영역에서 뭔가를 정의하고,

 

영역 경계의 좌표가 정수가 아니라는 것이 굉장히 불편할 수는 있을 것 같지만... (처음에는 나도 좀 불편했다.)

 

오히려 문제는 생각지도 못한 곳에서 발생했는데, 직접적으로 구현에 영향을 줄 정도는 아니었다.

 

기회가 되면 정리해야지.

 

 

아무튼 이렇게 기준을 정해놓으면 이후의 내용들을 정리하기가 비교적 편리하다.

 

이미지 resizing은 크게 두 가지 스텝으로 분리해서 볼 수 있는데, mapping과 resampling이다.

 

$h \times w$ input 이미지를 $H \times W$로 resizing 하고자 할 때 ($H > h$일 필요는 없다.),

 

생각해야 하는 것은 output 이미지의 [x, y] 위치가 input의 어디에 대응하는지?이다.

 

간편한 수식으로 어렵지 않게 변환이 가능하지만, 아래의 예시를 보면 조금 더 쉽게 이해가 가능하다.

 

$3 \times 3$에서 $4 \times 4$로 가는 경우를 생각하자.

 

일단 같은 크기의 정사각형 2개를 $3 \times 3$, $4 \times 4$로 쪼갠다.

3x3, 4x4로 쪼갠 정사각형.

두 정사각형을 겹쳐보면, output 이미지의 각 픽셀이

 

input 이미지의 어디에 대응하는지에 대한 비례 관계를 확인할 수 있다.

Output-input 대응 관계.

Boundary가 정수가 아니기 때문에, 계산이 조금 귀찮을 수는 있지만 구체적인 식은 아래와 같다.

 

$\textcolor{red}{x} = \frac{w(\textcolor{blue}{x'} + 0.5)}{W} - 0.5,$

$\textcolor{red}{y} = \frac{h(\textcolor{blue}{y'} + 0.5)}{H} - 0.5.$

 

여기서 $\textcolor{red}{x}$와 $\textcolor{red}{y}$는 input 이미지를 기준으로 한 좌표

 

$\textcolor{blue}{x'}$와 $\textcolor{blue}{y'}$는 output 이미지를 기준으로 한 좌표가 되는데

 

예를 들자면 output 이미지의 (0, 0)은 input 이미지의 (-0.12, -0.12)에 대응된다.

 

 

이제 드는 의문은, 그래서 (-0.12, -0.12)는 어디에 있는가?이다.

 

디지털 이미지는 regular grid 위에서 정의되기 때문에, img[0, 0]은 있어도 img[-0.12, -0,12]는 에러가 난다.

 

적당히 가장 가까운 픽셀의 값을 가져오면 되는 것 아닌가? 하는 의문이 들 수 있지만

 

(해당 방법이 nearest neighbor, 혹은 NN 보간법이다.)

 

그러면 자글자글한 계단 현상이 생기고 보기에 좋지 않다.

5x5 이미지를 NN과 bicubic으로 interpolation 한 결과. 출처는 위키피디아. https://en.wikipedia.org/wiki/Bicubic_interpolation

Bicubic 결과는 지나치게 뿌연 느낌이 든다고 생각할 수도 있지만, 뭐 일단은 그렇다.

 

이를 해결하기 위해서는 적절한 resampling이 필요한데, 다음 포스트에 정리해야겠다.

 

2021.05.04 - [분류 전체보기] - PyTorch로 MATLAB imresize (bicubic interpolation) 구현하기 (2) - resampling (interpolation)

 

PyTorch로 MATLAB imresize (bicubic interpolation) 구현하기 (2) - resampling (interpolation)

이전 글: 2021.05.03 - [코딩/PyTorch] - PyTorch로 MATLAB imresize (bicubic interpolation) 구현하기 (1) - 이미지 resizing 배경 지식 PyTorch로 MATLAB imresize (bicubic interpolation) 구현하기 (1) - 이..

sanghyun.tistory.com

 

+ Recent posts