argparse란 프로그램에 필요한 인자를 사용자 친화적인 명령행 인터페이스로 쉽게 작성하도록 돕는 라이브러리 이다. 즉, command 창에서 프로그램 내의 인자를 조절하게끔 도와준다. 참고로 argparse는 python에 기본적으로 내장되어 있다. 

 

command 창에서 [ python main.py --mode test ] 와 같이 --을 이용하여 추가적인 명령어를 사용하는 것을 흔하게 볼 수 있는데, 이것이 바로 argparse를 사용한 예시이다. 

 

argparse를 잘 알고있다면, 코드의 변화를 최소화 시키는 효율적인 코드를 작성할 수 있다. 

 

 

 

 

 

1. parser 의 전체적인 흐름

 

##main.py 

import argparse

#파서 만들기
parser = argparse.ArgumentParser(description='Argparse example')

#인자 추가하기
parser.add_argument('--learning_rate', '-lr', type=float, default=0.001, help='learning rate')
parser.add_argument('--mode', type=str, defualt='train', choices=['train', 'test'])
parser.add_argument('--no_gpu', action='store_true')
parser.add_argument('--epochs', type=int, required=True, help='number of epochs')

#내가 쓴 인자 저장
args = parser.parse_args()

#인자 사용
lr = args.lr

 

먼저 argparse를 import 한다. 

 

ArgumentParser를 이용하여 파서를 만들 때 'description= ' 키워드를 이용하여 해당 프로그램에 대한 설명을 넣는다. 보통 기능과 작동 방식에 대한 간략한 설명을 제공한다. 

 

그 후 add_argument() method를 이용하여 프로그램에 필요한 인자들을 정의한다. 

 

마지막으로 parse_args() method로 명령창에서 주어진 인자들을 파싱한다.  파싱이 무엇인지는 아래에 다시 설명하도록 하겠다. 

 

args.lr과 같이 args로부터 인자들을 받아 사용 가능하다. 

 

 

 

 

 

2. add_argument() 메서드

 

name or flags : 인자의 이름 지정

 

인자의 이름을 정할 때, --help, -h와 같이 fullname과 약자를 동시에 지정할 수 있다. 

##main.py 
parser.add_argument('--learning_rate', '-lr')

 

여기서 인자의 앞에 -- , - 가 붙어 있으면 optional 인자 (선택형 인자) , 붙어있지 않으면 positional 인자(위치형 인자) 이다. 위치형 인자는 필수적으로 입력해야 하는 인자이며, 선택형 인자도 required=True를 통해 필수로 입력하게끔 지정할 수는 있다.

 

인자가 여러개 있을 때, positional 인자는 순서를 지켜 입력해야 하며, optional 인자는 순서를 지키지 않아도 괜찮다. 

##main.py

import argparse

parser = argparse.ArgumentParser(description='Argparse example')
parser.add_argument('mode', type=str, choices=['train', 'test'])
parser.add_argument('-lr', type=float, default=0.01)
args=parser.parse_args()

print('args.mode : ', args.mode)
print('args.lr : ', args.lr)


##command 창

#가능
>python main.py train -lr 0.1
args.mode :  train
args.lr :  0.1

>python main.py -lr 0.1 train
args.mode :  train
args.lr :  0.1

#불가
#positional 인자는 인자 이름 없이 value값만 순서대로 써줘야 한다. 
>python main.py mode train -lr 0.1
usage: main.py [-h] [-lr LR] {train,test}
main.py: error: argument mode: invalid choice: 'mode' (choose from 'train', 'test')

 

 

 

type & default : 인자의 type, default 값 지정

 

인자가 int, float, str, bool 등 어떤 type인지 지정한다. default type은 str이다.

default=를 통해 사용자가 옵션을 주지 않았을 때 기본적으로 들어가는 값을 지정해 놓는 것이 좋다. 

##main.py 
parser.add_argument('--learning_rate', '-lr', type=float, default=0.01)

 

 

 

action : 인자의 저장 방식

 

우리가 command창을 통해 인자와 값을 적어 주었을 때, 이것을 코드가 해석하는 방식을 지정해 주는 것이 action이다. 여기서 드디어 파싱의 개념이 나온다.  기본값은 store 이다. 

 

  • store : action 인자의 default 값이다. 인자 이름+스페이스바 후에 나오는 값을 인자의 값으로 저장한다. = 파싱

#command에서 입력 후
>python main.py -lr 0.1

#main.py 에서 아래 코드가 호출 될 때
...
args = args.parse_args()
...
# parse_args() method 내에서 '-lr 0.1'.split() 가 일어나며 -lr에 0.1이 저장된다. 이것이 파싱이다. 

 

  • store_const : const 키워드 인자에 지정된 값을 저장한다. 

##main.py 

import argparse

parser = argparse.ArgumentParser(description='Argparse example')
parser.add_argument('-lr', action = 'store_const', const=0.1)
args = parser.parse_args()

print('args.lr : ', args.lr)


##command

>python main.py
args.lr : None

>python main.py -lr
args.lr : 0.1

 

  • store_true, store_false : 각각 True, False 값을 저장하는 store_cost의 일종이다. const를 따로 쓰지 않아도 const에 각각 True, False 값이 저장된다. 

##main.py 

import argparse

parser = argparse.ArgumentParser(description='Argparse example')
parser.add_argument('--gpu', action = 'store_true') #--gpu 불리면 True
parser.add_argument('--cpu', action = 'store_false') #--cpu 불리면 False
args = parser.parse_args()

print('args.gpu : ', args.gpu)
print('args.cpu : ', args.cpu )


##command

>python main.py
args.gpu :  False
args.cpu :  True

>python main.py --cpu
args.gpu :  False
args.cpu :  False

>python main.py --gpu
args.gpu :  True
args.cpu :  True

>python main.py --gpu --cpu
args.gpu :  True
args.cpu :  False

 

  • append : 리스트를 저장하고 인자가 불릴 때마다 리스트에 값을 추가한다. 

##main.py
...
parser.add_argument('--cls', action = 'append')
...


##command
>python main.py --cls cat --cls dog
args.class :  ['cat', 'dog']

 

  • append_count : 리스트를 저장하고 const 키워드 인자로 지정된 값을 리스트에 추가한다. 

##main.py 

import argparse

parser = argparse.ArgumentParser(description='Argparse example')
parser.add_argument('--str', dest = 'types', action = 'append_const', const = str)
parser.add_argument('--int', dest = 'types', action = 'append_const', const = int)
args = parser.parse_args()

print(args)


##command
>python main.py
Namespace(types=None)

>python main.py --str
Namespace(types=[<class 'str'>])

>python main.py --str --int
Namespace(types=[<class 'str'>, <class 'int'>])

 

  • count : 키워드 인자가 등장한 횟수를 계산한다. 

##main.py
...
parser.add_argument('--verbose', '-v', action='count', default=0)
...


##command

>python main.py -vv
args.verbose :  2

 

 

 

nargs : 인자의 값 개수 지정

 

보통 하나의 인자당 하나의 값이 들어온다. nargs는 한 인자에 여러개의 값이 들어올 수 있게끔 한다. 

  • nargs=1 : 1개가 들어간 리스트를 만드는 것으로 nargs를 지정하지 않은 기본값과는 다른다. 

  • nargs='?' : const, default와 함께 사용되며, command에서 '인자 값'으로 불러 값을 저장해주는 것도 가능하고, command에서 인자만 불러 const값으로 저정도 가능하며, command에서 부르지 않아 default값으로 저장도 가능하다. 

  • nargs='*' : 한 인자당 여러 값을 저장할 수 있다. 

  • nargs ='+' : 한 인자당 여러 값을 저장할 수 있으며 최소 1개 이상을 필요로 한다.  

##main.py
...
parser.add_argument('--zoo1', type=str, nargs=2)
parser.add_argument('--zoo_q', type=str, nargs='?', const='cat', default='dog')
parser.add_argument('--zoo_star', type=str, nargs='*')
...


##command

#nargs=2
>python main.py --zoo1 cat dog
args.zoo1 :  ['cat', 'dog']

#nargs='?'
>python main.py --zoo_q
args.zoo_q :  cat

>python main.py
args.zoo_q : dog

>python main.oy --zoo_q lion
args.zoo_q : lion

#nargs='*'
>python main.py --zoo_star cat dog lion
args.zoo_star :  ['cat', 'dog', 'lion']

 

 

 

choices : 인자 값의 범위 지정

 

정해진 값들 중 하나가 선택되어야 할 때 유용하게 쓰인다. 

##main.py
...
parser.add_argument('--mode', type=str, choices=['train', 'valid', 'test'])
...


##command

>python main.py --mode valid
args.mode :  valid

>python main.py --mode next
main.py: error: argument --mode: invalid choice: 'next' (choose from 'train', 'valid', 'test')

 

 

 

required : 선택형 인자를 필수로 

 

생략되면 안되면 인자에 대하여 필수로 지정하도록 한다. optional 인자에 쓰인다. 

parser.add_argument('--device', type=str, choices=['train', 'valid', 'test'], required=True)

 

 

 

help : 인자의 설명 제공

 

인자에 대한 설명을 쓴다. command창에서 --help를 입력할 경우 각 인자에 대한 help가 출력된다. 

##main.py
...
parser.add_argument('--device', type=str, choices=['train', 'valid', 'test'], required=True, help='choose 1 task')
...


##command
>python main.py --help
optional arguments:
  -h, --help            show this help message and exit
  --device {train,valid,test}
                        choose 1 task
  

 

 

 

 

 

 

3. parse_args() 

 

command 창에서 받은 문자열을 객체로 변환하여 namespace의 attribute로 설정하는 것이 parse_args()를 호출함으로써 이루어진다. namespace는 object의 서브 클래스로 vars(args)를 통해 args를 딕셔너리 형태로 볼 수 있다. 

 

'Python' 카테고리의 다른 글

Pandas  (0) 2021.03.08

이번 포스팅에서는 2019년 ICCV에서 발표된 wavelet style transfer 이용한 super-resolution 논문을 리뷰해 보려 한다.

 

0 Abstract 부터 2 Related work는 배경설명 및 다른 네트워크들간의 비교이며 본 논문의 모델은 3 proposed method에서 소개한다. 

 

 

 

<간단 요약>

 

1. EDSR, CX를 이용하여 성질이 다른 두 이미지를 얻는다. 

2. 두 이미지를 stationary wavelet transform(SWT)을 통해 low frequency sub domain과 high frequency sub domain을 구분한다. 

3. high frequency sub domain를 pixel domain이 아닌 wavelet domain에서 style transfer를 함으로써 high frequency perceptuality를 개선하였다. 

4. EDSR의 low frequency sub domain을 압축된 VDSR에 통과시켜 objective quality를 높였다. 

5. content loss와 style loss 모두를 고려하는 새로운 loss function을 제안했다. 

6. 그 결과 high frequency detail이 살아났으며 선명한 텍스쳐와 구조를 얻을 수 있었다. 즉, PSNR과 NRQM(perceptuality)를 모두 높일 수 있었다. 

 

 

 

0 Abstract

 

super-resolution은 LR(low-resolution, 저해상도) 이미지로부터 HR(high-resolution, 고해상도) 이미지를 복원하는 것을 말한다. 최근 super-resolution 부문의 이슈는 low distortion과 high perceptual quality가 trade-off 관계라 두 관점을 모두 만족시키기 어렵다는 것이다. 

 

지난번에 언급한 SRCNN, VDSR, EDSR 등의 논문들은 모두 LR이 HR에 최대한 가까워 지도록 학습해 distortion은 적지만(PSNR은 높지만) 엣지가 선명하지 않고 디테일이 사라지며 질감이 무너지는 것과 같이 사람의 눈으로 보았을 때 만족스럽지 않은(low perceptual quality) 결과를 얻었다. 

 

이런 low perceptual quality 문제점을 최근 GAN 을 기반으로 한 모델이 photo realistic한 이미지를 만들어 내며 잘 보완해 주고 있다. 그러나 이 논문에서는 wavelet domain style transfer(WDST)를 이용하여 GAN을 기반으로 한 모델보다 perception-distortion(PD) trade-off 를 더 잘 보완할 수 있었다고 한다.

 

2D stationary wavelet transform(SWT)를 이용하여 이미지의 low-frequency 부분과 high frequency 부분을 나눠 각각에 대해 독립적으로 개선시킨 후 합친다는 것이 이 논문의 취지이다. low-frequency sub-bands를 이용하여 objective quality를 개선하고(low distortion), high-frequency sub-bands를 이용하여 perceptual quality를 개선할 수 있다. 

 

 

 

1 Introduction  &  2 Related work

 

앞서 말했듯이 기존의 연구는 mean square error(MSE)를 낮춰 objective image quality를 높이는 데 집중한 연구와 adversarial training을 통해 perceptual loss를 낮춰 perceptual image quality를 높이는 데 집중한 연구로 나뉜다. 아래의 그림1을 보면 PSNR을 높이는 데 집중한 파란색 네트워크들과 perceptuality(NRQM metric)를 높인 초록색 네트워크들이 명확히 구분된다.

 

objective quality를 높인 그림1의 파란색 네트워크들은 대부분 MSE를 minimize하여 reconstructed image와 ground truth image 사이의 간극을 좁힌다. 이는 high frequency detail들이 사라져 엣지가 blur된다는 문제점을 야기한다. 

 

perceptual quality를 높인 그림1의 초록색 네트워크들은 VGG loss와 adversarial loss의 weighted sum으로 구성된 perceptual loss를 낮추도록 학습한다. VGG loss는 reconstructed image와 ground truth image 간의 perceptual similarity를 높이는 데 효과적이며, adversarial loss는 reconstructed image를 realistic하게 만든다.

 

그림1. PSNR과 NRQM의 trade-off 관계 속 PSNR에 집중한 네트워크(파랑)와 perceptuality에 집중한 네트워크(초록)와 절충점을 찾는 네트워크(주황)

 

위 trade-off 관계를 보완하기 위하여 adversarial loss와 MSE를 활용하는 SRGAN-MSE, ENet 등의 그림1의 주황색 모델이 도입되었으나 unstable하다는 지적이 있었다.  SRGAN-MSE는 MSE loss와 adversarial loss를 합쳐 사용하는데, adversarial loss는 결과 이미지의 high frequency detail들을 살려냈지만, 그것이 정확한 위치에 있지 않아서(ground truth image와는 사뭇 다른 이미지여서) MSE distortion은 증가되는 문제점이 있었다. 이런 instability 문제점을 보완하기 위해 texture matching loss를 도입하였지만 여전히 blocking과 noisy artifact가 생겼다. 

 

원치 않는 artifact들이 생기는 문제점을 보완하기 위하여 ESRGAN이 등장하였다. PIRM challenge에서 우승한 ESRGAN은 MSE를 낮추는 프로세스와 perceptual quality를 높이는 프로세스를 분리하기 시작한다. 두개의 독립적인 네트워크는 마지막에 interpolation하여 합쳐진다. 그러나 network interpolation은 두 네트워크가 완전히 똑같은 구조를 가지고 있어야 하기에, 분명히 다른 성질을 갖고있는 두 부분에 대해 각각의 성장 가능성을 저해한다는 단점이 있다. 

 

따라서 network interpolation이 아닌 분리된 네트워크에서 나온 image들의 fusion을 고안해 보았을 때, 네트워크 구조의 유연함을 증폭할 수 있다는 점이 큰 장점이다. 최근 Deng 이 style transfer를 이용한 두 이미지의 fusion을 제시하였다. 그러나 style transfer는 pixel domain상에서의 작업이므로 이미지의 structure와 texture를 동시에 보존한다는 것은 매우 까다롭다. 그림2를 보면 objective and perceptual quality 의 trade-off관계 속에서 optimize를 시도하였으나 여전히 발전이 필요해 보인다. 

 

그림2. 네트워크 결과 비교

 

본 논문에서는 pixel domain이 아닌 frequency domain에서 low-frequency와 high-frequency로 분리함으로써  Deng의 부족한 점을 보완할 수 있었다. 새로운 SISR(single image super-resolution) method를 선보이는 것은 아니나, 기존의 성질이 다른 두 네트워크를 사용하여 얻은 두 결과 이미지를 fusion함으로써 best tradeoff를 찾았다는 것에 의의가 있다.  

 

본 논문을 SRGAN-MSE 논문과 비교하였을 때 deep network를 train하지 않아 stability 걱정이 없으며, ESRGAN과 비교하였을 때 네트워크 구조가 훨씬 flexible하고, Deng과 비교하였을 때 wavelet domain에서의 style transfer를 구현하여 새로운 테크닉을 선보였다는 점에서 좋은 결과를 얻을 수 있었다.

 

 

 

3 Proposed method

 

Stationary wavelet transform

 

기존의 discrete wavelet transform(DWT)는 shift invariant하지 않기 때문에 convolution을 적용할 수 없다. 따라서 DWT에서 downsampling operation을 제거한 stationary wavelet transform(SWT)을 적용하였다. 2D SWT는 그림3의 수식과 같이 1D wavelet decomposition인 H0(low-pass filter)과 G0(high-pass filter)의 z transform으로 얻어진다. 

 

2D SWT 수식
그림3. 2D SWT of image X with H0 and G0 as the low-pass and high-pass filters, respectively

2D SWT는 이미지를 여러개의 sub-bands로 나누는데, 이는 1개의 low-frequency sub-bands(LL)와 여러개의 high-frequency sub-bands(LH, HL, HH)로 구성된다. 2 level decomposition을 진행한 후, input X는 7개(3*2high+1low)의 상태로 나뉘어 지며 LH, HL, HH는 각각 horizontal, vertical, diagonal detail을 나타낸다. level2는 level1의 LL로부터 생성된다. 

 

 

Motivation

 

여기서 중요한 insight는 LL을 나타내는 low-frequency sub-band가 이미지의 objective quality에, 나머지 LH, HL, HH와 같은 high-frequency sub-bands가 perceptual quality에 각각 지대한 영향을 미친다는 것이다. 이를 증명하기 위해 CX로부터 얻은 high perceptual quality를 가진 Ap 이미지와 EDSR로부터 얻은 high objective quality를 가진 Ao 이미지를 얻었다. Ap, Ao 이미지를 1 level decomposion하여 얻은 sub-bands를 관찰해 보자. 

 

그림4. histograms of different sub-bands of Ap, Ao, Ground Truth

 

그림4를 보면, Ao의 LL sub-band histogram이 Ap의 LL보다 ground truth LL과 더욱 유사함을 볼 수 있고, Ap의 LH, HL, HH sub-bands histogram이 Ao의 LH, HL, HH보다 ground-truth의 LH, HL, HH와 더욱 분산이 유사함을 볼 수 있다. 

 

다음으로 정량적인 분석을 해보자. 성능비교를 위해 objective quality에는 PSNR(peak signal-to-noise-ratio)를, perceptual quality에는 NRQM을 각각 평가지표로 사용하였다. (PSNR과 NRQM은 모두 높을수록 좋은 성능임을 나타낸다.)

 

Ao의 LL과 Ap의 LH, HL, HH를 합친 것을 Ap~, Ap의 LL과 Ao의 LH, HL, HH를 합친 것을 Ao~라고 할 때, Ap, Ap~, Ao, Ao~의 PSRN, NRQM을 출력해 보았다. 표를 보기 전, 앞서 말했던 맥락을 참고하였을 때, Ap~Ao의 LL을 받았으므로 Ap보다 더 좋은 objective quality를 갖고 있어야 한다. 따라서 PSNR이 높아지고 NRQM이 조금 낮아질 것을 예상할 수 있다. Ao~Ap의 LL을 받았으므로 Ao보다 더 좋은 perceptual quality를 갖고 있어야 하나, LL은 다른 sub-bands에 비해 perceptual 정보를 적게 갖고 있으므로 NRQM이  조금 높아지고(Ap에 못미치게) PSNR은 낮아질 것을 예상할 수 있다.   

 

Table1

 

 

 

여기까지 보았을 때, 우리는 LLo가 objective quality를 높일 sub band이며, 나머지 sub band가 LHp, HLp, HHp에 가까울수록 perceptual qualiity가 높아질 것을 알 수 있다. 

 

 

 

그림5. the framework of our method

 

Low-frequency sub-band enhancement (LSE)

 

이제 드디어 네트워크의 뼈대를 살표볼 차례이다. perceptuality를 높이는 네트워크(CX)에서 얻은 아웃풋(Ap)과 objectivity를 높이는 네트워크(EDSR)에서 얻은 아웃풋(Ao)을 각각 SWT(wavelet transform)하여 frequency domain으로 분리한다.

 

low frequency sub band인 LLo는 objective quality를 더욱 강화하기 위한 LSE 작업을 거친다. LSE는 VDSR의 구조를 차용한 작은 네트워크로, 6개의 convolution layer와 Relu를 지나 마지막에 input을 더해 output을 출력한다.  모든 layer의 filter 는 64개, filter size는 3x3이며 loss는 l2 norm을 이용한다. 

 

 

Wavelet domain Style transfer (WDST)

 

SWT 하여 얻은 LHo, HLo, HHo를 style transfer하여 LHp, HLp, HHp에 비슷하게 바꿈으로써 perceptual quality를 높이고자 한다. 따라서, LHp를 style input으로, LHo를 content input으로 하여 LHo의 content를 유지하며 LHp의 style을 갖는 아웃풋 LHr을 생성할 수 있도록 style transfer를 진행한다.

 

기존 style transfer와의 차이점은 wavelet coefficients를 input으로 넣는다는 것이다. wavelet transform한 결과는 음수도 있고 1보다 큰 숫자도 있기 때문에 VGG19 network에 input으로 넣기 전 0-1로 normalize해야한다.

 

 

수식1. style transfer loss

 

normalization 후 각각의 high-frequency sub-band pair에 대하여 새롭게 정의한 loss를 줄이는 방향으로 아웃풋을 만들어 낸다. loss function은 수식1에서 볼 수 있듯 content loss(Lc), style loss(Ls), l1 loss의 weighted sum으로 구성된다. l1 norm loss를 추가한 이유는 wavelet coefficients에 0 값이 많기 때문이다(그림4의 히스토그램 참고). 데이터가 sparse할 때(0값이 많을 때), l2 loss보다 l1 loss를 사용하는 것이 더 효과적인데,  다음 사이트에 설명이 잘 나와있어 첨부한다. 간단히 말하자면 l2 loss는 항상 양수인 값들이 존재하기 때문에 sparse한 상황에서 0을 향해 optimize되는 데에 한계가 있다. 

https://stats.stackexchange.com/questions/45643/why-l1-norm-for-sparse-models

 

 

수식2. content loss
수식3. style loss의 weighted sum
수식4. 각 층의 style loss

 

content loss와 style loss는 pre-trained VGG19 네트워크를 이용하여 여러 feature를 중간에서 뽑아 loss를 계산한다(그림5의 WDST). content loss는 MSE loss로 conv2-2 에서 LHo와 LHr의 feature를 한번 뽑아 content loss를 계산하며 수식2의 N은 layer L의 feature map 수, M 은 feature map의 weight*height를 나타낸다.

 

style loss는 Relu1-1, Relu2-1, Relu3-1, Relu4-1, Relu5-1 총 5개의 층에서 LHp와 LHr의 feature를 뽑아 weighted sum하여 style loss를 계산한다(수식3). style loss는 gram matix의 MSE loss를 계산함으로써 LHr과 LHp 간의 correlation을 계산한다. 

 

이렇게 high frequency sub-bands LHr, HLr, HHr를 얻었으면 de-normalization해주어 본래의 data 분포를 갖게끔 만들어준다. 앞서 LSE를 통해 강화된 LLr과 LHr, HLr, HHr를 합쳐 2D ISWT(inverse stationary wavelet transform)하면 결과 이미지를 얻을 수 있다. 

 

 

 

 

4 Numerical Results

 

Experimental setup

 

-2D SWT의 wavelet filter로는 bior2.2를 사용

-wavelet decomposition level은 2

-SGD optimizer 사용 (LSE)

-batch size 64 (LSE)

-basic learning rate 0.01, momentum 0.9 (LSE)

-ratio between the content loss and the style loss 10^(-3) (WDST)

-ratio between the content loss and the l1 norm loss 10^(-5) (WDST)

-style loss의 각 layer weight 0.2 (WDST)

-first layer maximum iteration 5000, second layer maximum iteration 1000 (WDST)

-AoEDSR을 통해 얻음 

-ApCX를 통해 얻음

-dataset : Set5, Set14, BSD100, Urban100, PIRM

 

 

Wavelet filter sensitivity

 

그림6. 다양한 필터 적용 결과
                                             그림 7. haar, db2, bior2.2                                              
그림7. coif2, db4
 그림7. rbior2., bior4.4

 

본 논문에서는 7가지의 wavelet filter를 사용하여 PSNR, SSIM, NRQM을 비교하였다. 다른 wavelet function들과는 다르게 rbior2와 bior4.4는 decomposition과 reconstruction시 사용되는 wavelet function의 모양이 다르다. PSNR을 높이는 데에는 haar, db2가 효과적이었으며, NRQM을 높이는 데에는 bior4.4가 효과적이었다. 

 

 

나는 여기까지 읽었을 때, "그래서 이렇게 하면 좋다는건 알겠는데, EDSR과 CX를 그냥 합친것보다 뛰어난지는 어떻게 알지?" 라는 의문이 들었다. 

 

 

Content and Style inputs sensitivity

 

그림8. interpolation보다 우세함을 보임

 

그림8은 objectivity가 뛰어난 네트워크에서 뽑은 결과 Ao와 perceptuality가 뛰어난 네트워크에서 뽑은 결과 Ap의 단순 interpolation 결과와 본 논문의 방법을 비교한 것이다. PSNR과 Perceptual score모두 interpolation한 결과보다 뛰어난 성능을 보임을 알 수 있다. 

 

 


드디어 논문 리뷰가 끝이 났다!! 다음에는 cycleGAN을 다뤄볼까...

 

 

 

 


REFERENCE

1. Xin Deng et al, "Wavelet Domain Style Transfer for an Effective Perception-distortion Tradeoff in Single Image Super-Resolution", 2019 ICCV

2. l1 sparsity : https://stats.stackexchange.com/questions/45643/why-l1-norm-for-sparse-models

3. wavelet function : http://wavelets.pybytes.com/

오늘은 내가 주로 사용하는 git 명령어들을 정리해 보도록 하겠다! local에서 작업한 것을 remote로 보내는 법, remote를 local에 가져와서 작업하는 법, 자주 사용하는 명령어 등에 대하여 적어보겠다. 

 

그 전에, github 사용을 위한 사전지식을 몇개 말해보자면, 

 

 

 

<GitHub 사전지식>

 

1. 로컬 저장소(local)와 원격 저장소(remote)

git 저장소는 자신의 컴퓨터인 로컬 저장소와 서버에 있는 원격 저장소로 나뉜다. local에서 작업한 것은 remote로 push해줘야만 변경사항이 서버에 반영된다. 

 

2. add, commit, push

자신이 작업한 내용을 remote 저장소에 반영하기 위해서는, 변경사항을 추가하고(add), local에 저장하고(commit), remote에 업로드(push) 해야한다.

 

3. branch

여러 개발자들이 공동으로 작업할 수 있게 기본 master branch에서 새로운 가지를 만들어 독립된 공간에서 작업을 수행할 수 있다. 이 때 주기적으로 변경사항을 합치는 것이 필요하다.  

 

4. pull

remote에 있는 내용을 local에 받는 과정이다. 이때 현재 자신의 branch가 어디인지 확인을 잘 하고 pull하도록 한다. 만약 자신의 local에 변경사항이 있다면 pull할 시 에러가 나므로 add, commit을 진행한 후 pull하거나 stash하여 자신의 변경사항을 다른곳에 저장한 후 pull하도록 한다. 

 

5. 기본 흐름

github 공간 만들기(clone, init) => 파일 작성 => 파일의 변경사항 임시저장(add) => local에 저장(commit) => remote에 업데이트(push) => 로컬 업데이트(pull) => add => commit => push ==> pull...(반복)

 

6. 로컬 작업 시작 전 무조건 pull

remote 저장소에 변경된 사항이 있을 수 있기에(여러명이서 작업시) 무조건 파일을 건들기 전에 pull하도록 한다. 안그러면 conflict이 일어나 수동으로 고쳐야한다.

 

 

 

<자주 사용하는 명령어>

 

1. git clone https://~

깃허브에서 project를 만든 후 git clone하여 local에도 작업공간을 만든다. 

 

     1-1. git init 

깃허브에서 project를 만들어 clone하지 않고 컴퓨터에서 작업을 먼저 시작했을 때 저장소를 생성한다. 

 

2. git branch

현재 branch(*표시 되어있는) 및 local branch 확인

 

     2-1 git branch jayeon

jayeon branch 만들기

 

     2-2 git branch -d jayeon

jayeon branch 삭제

 

     2-3 git branch -D jayeon

jayeon branch 강제 삭제(merge가 되지 않은 branch에 대한 삭제)

 

     2-4 git push origin -d jayeon

remote 서버에 있는 jayeon branch 지우기

 

    2-5 git push origin :jayeon

로컬에서 jayeon branch 지웠을 때 remote에도 그 변경사항 반영하기

 

3. git checkout jayeon

jayeon branch로 이동

 

     3-1 git checkout -b jayeon

jayeon branch 만들고 이동

 

4. git status

현재 상태(add전후, commit 전후 등 확인 가능) 및 브랜치 확인

 

5. git add, git commit, git push, git pull

 

6. git log

로컬 저장소의 commit history 보기

 

     6-1. git log -n 10

10개만 보기

 

     6-2. git log --oneline --graph

log 그래프로 확인

 

7. git checkout -- jayeon.py

변경된 jayeon.py 되돌리기

 

8. git rm jayeon.py

jayeon.py 로컬, git 저장소 모두에서 삭제

 

     8-1. git rm --cached jayeon.py

jayeon.py를 git 저장소에서 삭제 (로컬은 유지) 

 

 

 

<local ==> remote : remote에서 merge하기>

 

1. git checkout -b jayeon

jayeon이라는 branch를 만든 후 jayeon으로 이동

 

2. git push origin jayeon

remote에 jayeon branch 생성

 

3. git branch --set-upstream-to=origin/jayeon jayeon

remote와 local branch 연결

 

(작업 후)

 

4. git add .

변경사항 모두( . )임시저장

 

5. git commit -m "메세지 내용"

commit 하여 local 저장소에 반영

 

6. git push

remote 저장소에 변경사항 반영-jayeon에 반영

 

7.홈페이지에서 merge 진행

 

 

 

<local ==> remote : local에서 merge하기>

 

1. git checkout -b jayeon

jayeon이라는 branch를 만든 후 jayeon으로 이동

 

(작업 후)

 

2. git add .

변경사항 모두( . )임시저장

 

3. git commit -m "메세지 내용"

commit 하여 local 저장소에 반영

 

4. git checkout master

master branch로 이동

 

5. git merge jayeon

jayeon branch를 합치려는 target branch 로 이동 후 local에서 target에 jayeon branch merge

 

6. git push

remote 저장소에 변경사항 반영

 

 

 

<remote ==> local>

 

1. git branch -a

local branch 및 remote branch 확인

 

2. git checkout -b jayeon origin/jayeon

remote의 origin/jayoen branch와 이어지는 local jayeon branch 생성

 

(작업 후)

 

3. git add .

변경사항 모두( . ) 임시저장

 

4. git commit -m "메세지 내용"

commit 하여 local 저장소에 반영

 

5. git push 

remote 저장소에 반영 -jayeon에 반영

 

 

 

<그 외>

 

1. git stash 

지금의 상태 임시 저장. commit 해야하는 것이 있어 pull이 안될 때 주로 사용함.

 

2. git stash list

stash 목록 확인

 

3. git stash apply

가장 최근의 stash 가져오기

 

4. git stash apply [stash이름]

[]stash 가져오기

 

5. git stash drop

가장 최근의 stash 삭제

 

6. git stash clear

모든 stash 삭제

지금까지 딥러닝을 이용한 화질개선(super-resolution)에 관한 여러 논문들이 소개되어 왔다. 이 포스팅에서는 지금까지 어떤 논문들이 있었는지(2014-2018) 간략하게 소개하고, 다음의 소수 논문만을 간단히 살펴보도록 하겠다. 

 

  1. SRCNN

  2. VDSR

  3. SRGAN(SRRseNet)

  4. EDSR, MDSR

 

 

 

1. SRCNN

Chao Dong et al, "Image Super-Resolution Using Deep Convolutional Networks", 2014 ECCV

Fig1. SRCNN architecture

SRCNN은 super-resolution 분야에 딥러닝을 최초로 적용한 논문이다. 방법은 보시다시피 매우 간단하다. Low-resolution image에 bicubic을 이용하여 HR 사이즈와 동일하게 키운 후 이미지의 size를 유지하며 convolution network를 3번 통과하여 이미지의 화질을 개선시킨다. 매우 간단한 방법임에도 불구하고 좋은 성능을 보인다. 

 

 

 

 

2. VDSR

Kyoung Mu Lee et al, "Accurate Image Super-Resolution Using Very Deep Convolutional Networks", 2016  CVPR

Fig2. VDSR architecture

VDSR은 아주 deep 한 네트워크로 VGG-net에서 영감을 받았다고 한다. 20개의 레이어를 사용하였으며 계단식의 작은 필터들을 여러번 사용함으로써 이미지 전반적으로 contextual information을 잘 활용할 수 있었다고 한다. 또한 adjustable gradient clipping을 사용함으로써 104배의 큰 learning rate(SRCNN과 비교하였을 때)가 느린 수렴 속도를 보완해 주었다. 위 그림에서 마지막에 한번의 residual 이 들어감을 알 수 있는데, 이 여파인지 VDSR 이후의 super-resolution 논문들은 residual block을 적극 활용하고 있다.  

 

 

 

 

3. SRResNet(SRGAN)

Christian Ledig et al, "Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network", 2017 CVPR

Fig3. SRGAN architecture

지금까지 super-resolution의 accuracy나 speed면에서는 크게 성능이 좋아졌으나 high frequency를 갖는 texture detail들이 잘 살지 않는다는 단점이 있었다. SRGAN 논문에서는 GAN의 형식을 사용함으로써 texture를 보다 잘 살린다는 장점이 있다.

 

기존의 논문들은 MSE loss와 PSNR metric을 동시에 사용하여 이미지의 화질개선 성능을 평가했기에 PSNR 수치는 높으나 엣지가 선명하지 않은 blur된 이미지가 도출되었다면, 본 논문에서는 adversarial loss 와 content loss를 사용하는 perceptual loss function을 적용함으로써 사람이 보기에 더 좋아보이는 이미지를 만든다는 점에서 큰 차이가 있다. perceptual loss는 VGG network의 high-level-feature maps를 사용하였다.

 

위 그림에서도 확인 가능하듯 generator network에서 ResNet과 skip-connection을 적극 활용함으로써 time과 memory 절약에 좋은 성능을 보였다. 

 

위 논문에서도 알 수 있듯, loss function과 metric은 이미지 개선에서 큰 차이를 불러 일으킨다. 기회가 된다면 지금까지 deep learning에 사용되는 loss function들(l1, l2, cross entropy loss, VGG loss, perceptual loss 등)과 metric(PSNR, SSIM 등)의 상관관계를 분석해보고 각 loss fuction들과 metric들이 가지는 장단점들에 대하여 정리해보는 포스팅을 진행해 보겠다. 

 

 

 

 

4. EDSR, MDSR 

Bee Lim et al, "Enhanced Deep Residual Networks for Single Image Super-Resolution", 2017 CVPRW

Fig4. comparison of residual blocks
Fig5. EDSR(single-scale)architecture                                                 Fig6. MDSR(multi-scale) architecture

 

 

EDSR, MDSR 논문에서는 residual block에서 불필요한 요소들을 제거했다는 것이 큰 특징이다. 기존의 super-resolution 논문들은 MSE, L2 loss를 사용한 반면, 본 논문에서는 L1 loss를 사용하였다. 또한 기존의 논문들에서는 한번에 한 종류의 scale에 대하여 학습시켰다면, MDSR에서는 x2, x3, x4배율 모두 upscale이라는 맥락 아래에서 weight들을 공유할 수 있을 것이라 생각하여 다양한 배율을 동시에 학습하는 multi-scale deep super-resolution system을 도입하였다.

 

Fig4에서 볼 수 있듯, 본 논문에서는 residual module에 포함되어 있던 batch normalization을 제거하였다. BN layer에서 feature들을 normalize하기 때문에 네트워크 내의 유연성 즉, 개선 가능성을 억제한다고 보았기 때문이다. 실제로 이런 간단한 변경이 디테일을 살리는 부분에서 도움이 되었다고 한다. SRResNet과 비교하였을 때, BN layer를 제거함으로써 GPU memory를 약 40% 절약할 수 있었다고 한다.

 

Fig5의 EDSR architecture는 SRResNet의 baseline을 따랐으나, residual block 밖의 ReLU activation을 제거하였다는 점이 다르다. Fig6의 MDSR architecture는 parameter가 공유되는 중간의 16층 ResBlock과 scale specific하게 진행되는 앞, 뒤 module로 구성된다. 네트워크의 머리부분에 해당하는 pre-processing module은 5x5 kernel의 2개의 residual block으로 구성된다. 상대적으로 큰 kernel를 이용함으로써 scale-specific한 부분들을 잡아낼 수 있었다고 한다. 또한 마지막 부분의 upsampling module을 parallel하게 진행시킴으로써 x2, x3, x4의 결과를 각각 얻을 수 있다.   

 


 

앞서 소개한 논문들 후에 super-resolution을 다루는 논문들이 많이 나왔다. 종류별로 잘 정리되어있는 자료를 찾아 첨부한다.

 

Fig7. super-resolution papers

2019년 9월 17일에 발행된 논문의 자료임으로 대강 2014-2018(2019초)의 논문들이 정리되어 있다고 생각하면 될 것 같다. 

 

 

 


Reference

 

 

1. Wenming Yang et al, "Deep Learning for Single Image Super-Resolution: A Brief Review",  https://arxiv.org/pdf/1808.03344.pdf

2.  Saeed Anwar et al, "A Deep Journey into Super-resolution: A Survey",  https://arxiv.org/pdf/1904.07523.pdf

3. Chao Dong et al, "Image Super-Resolution Using Deep Convolutional Networks", 2014 ECCV

4. Kyoung Mu Lee et al, "Accurate Image Super-Resolution Using Very Deep Convolutional Networks", 2016  CVPR

5. Christian Ledig et al, "Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network", 2017 CVPR

6. Bee Lim et al, "Enhanced Deep Residual Networks for Single Image Super-Resolution", 2017 CVPRW

 

 

 

DICOM 이란 Digital Imaging and Communications in Medicine 으로 이 포스팅은 의료영상 이미지 중 CT 이미지 전처리에 대해 다루려고 한다. 

 

.dcm(dicom)파일로 저장된 의료영상 이미지는 float array를 따로 빼내어 딥러닝에 사용한다.

float array를 따로 저장할 수 있는 확장자가 많지만 필자는 주로 tiff 파일로 저장하여 사용한다. 

 

다이콤파일에서 픽셀 어레이만 뽑아서 가져온 후 쌓아서 저장하면 다 아닌가?

라고 생각할 수 있지만, 그게 다가 아니다!

 

dcm폴더 예시

 

3D영상의 경우, 위처럼 한 폴더 내에 여러개의 .dcm파일이 들어있다. 위 폴더에는 134개의 dcm파일이 들어있음을 볼 수 있다. 

그런데, 파일 번호 순서대로 픽셀 어레이를 쌓으면 안된다. 번호 순서대로 쌓으면 위치순서가 엉망진창임을 확인할 수 있다. dcm파일을 읽은 후 InstanceNumber를 뽑아 sort한 후 쌓도록 하자.

 

from glob import glob
import os
import numpy as np
import pydicom
import skimage.external.tifffile import imsave, imread, imshow

dicom_dir = '/public_data/LIDC-IDRI/LIDC-IDRI-0001/01-01-2000-30178/3000566-03192'

dcm_files = glob(os.path.join(dicom_dir, '*.dcm'))
dcm_files = [pydicom.dcmread(s) for s in dcm_files]

dcm_files.sort(key = lambda x : int(x.InstanceNumber))

dsRef = dcm_files[0]

 

 

dcm 파일을 읽으면, 그 안에 많은 key들이 있다. dimension(key : Rows, Columns) 정보 뿐만 아니라, ct vendor사, pixel간 거리(key : PixelSpacing, SliceThickness), pixel array 등이 있으니 필요한 정보가 있으면 key를 뽑아본 후 정보를 확인해 보는 것도 좋은 방법이다.

 

"""
see all keys in dicom files
"""

print(dsRef)
print(dsRef.ImageType)

for key in dsRef.__dir__():
	print(key)
    print(key, dsRef.data_element(key))
   

"""
get dimension and thickness information from the dicom file
"""

dims = (len(dcm_files), int(dsRef.Rows), int(dsRef.Columns))
print('dims(z,x,y) : ', dims)

spacing = (float(dsRef.SliceThickness), float(dsRef.PixelSpacing[0]), float(dsRef.PixelSpacing[1]))
print('thickness(z,x,y) : ', spacing)

 

 

이제 dimension정보를 얻었으니 sort한 dicom_files를 토대로 pixel_array값을 불러오도록 하자. 

 

recon_ct = np.zeros(dims, dtype = dsRef.pixel_array.dtype)

for i, df in enumerate(dicom_files):
	try : 
    	recon_ct[i,:,:] = df.pixel_array
    except : 
    	print(str(i+1).zfill(5) + '.dcm', '**pixel_array_shape Error')

 


 

HU(Hounsfield Unit)

CT영상을 처리하기 위해서는 Hounsfield Units(HU)이 무엇인지 알아야 한다. 이는 X선이 몸을 투과할 때 부위별 흡수정도를 표시한 지표로 CT number라고 부르기도 한다. 물을 0으로 고정하였을 때의 상대적인 흡수량이라고 생각하면 된다(물의 attenuation coefficient에 대한 상대적 비율 * 1000). 우리는 dicom 파일로부터 뽑은 픽셀 어레이를 우선 HU 단위로 정규화 해야한다. 이 때 필요한 것은 dcm파일의 Rescale Slope과 Rescale Intercept 이다. 

 

CT이미지의 경우, 디스크에 저장되는 값과 메모리에 올라오는 값의 표현이 다르게 설정되어 있다. HU는 음수를 포함한 정수값이지만, CT이미지는 일반적으로  unsigned integer인 부호없는 정수로 저장되기 때문이다. 아래의 식은 메모리(output)와 디스크(stored value)에 저장되어 있는 픽셀값의 linear transformation 관계식이다. 

 

(output) = (rescale slope) * (stored value) + (rescale intercept) 

 

우리는 dicom 파일로부터 stored value, rescale slope, rescale intercept을 얻어 위의 식을 계산함으로써 HU에 맞는 값을 얻을 수 있다. (참고 : https://blog.kitware.com/dicom-rescale-intercept-rescale-slope-and-itk/)

 

"""
Hounsfield Units(HU)
"""

recon_ct = recon_ct.astype(np.int16)
recon_ct[recon_ct == -2000] = 0

intercept = dsRef.RescaleIntercept
slope = dsRef.RescaleSlope

if slope != 1:
	recon_ct = slope * recon_ct.astype(np.float64)
    recon_ct = recon_ct.astype(np.int16)
    
recon_ct += np.int16(intercept)

 

 

HU에 맞는 값을 얻은 후, 무엇을 보고싶은지에 따라 알맞게 window width와 window level을 조정한다. 예를 들어, -1000~+400까지만 보고싶다면, upper bound = 400, lower bound = -1000으로 설정한 후, 0-1로 normalize하여 사용한다.  이렇게 새로 정의된 recon_ct 를 tiff file로 원하는 path에 맞춰 저장하면 된다.

 

MIN_BOUND = -1000.0
MAX_BOUND = 400.0

recon_ct = (recon_ct - MIN_BOUND) / (MAX_BOUND - MIN_BOUND)
recon_ct[recon_ct>1] = 1.0
recon_ct[recon_ct<0] = 0.0

recon_ct = np.array(recon_ct, dtype = np.float32)

#save array2tiff
target_path = 'D:/data/tiff'
target_name = 'example.tiff'
target_path = os.path.join(target_path, target_name)

imsave(target_path, recon_ct)

 

 

+ Recent posts