기억의 기록(나의 기기)

[Computer Vision] Super Resolution 구현 - SRCNN 본문

팀 프로젝트

[Computer Vision] Super Resolution 구현 - SRCNN

황경하 2024. 7. 22. 16:11
반응형

Computer vision - Super Resolution 구현

안녕하세요, 데이터 과학 전공하고 있는 황경하입니다.

오늘은 동아리 학우분들과 새로 시작한 프로젝트에서 제가 맡은 Super Resolution 구현 과정을 포스팅해보려 합니다. 이번 포스팅을 포함하여 관련 포스팅에는 제가 프로젝트를 하면서 겪은 고충들과 대처법, 극복 스토리들을 다룰 예정입니다. 이 포스팅이 작게나마 저와 같은 고충을 겪는 사람들에게 도움이 되었으면 합니다.

 

프로젝트의 아이디어와 데이터는 데이콘 공모전을 통해 수집하였습니다.

공모전: https://dacon.io/competitions/official/236251/data

 

저해상도 조류 이미지 분류 AI 경진대회 - DACON

분석시각화 대회 코드 공유 게시물은 내용 확인 후 좋아요(투표) 가능합니다.

dacon.io

(이미 종료된 공모전이기에 스터디 개념으로 시작하며, 공유된 코드들은 성능 비교용으로만 참고할 예정입니다.)

Super Resolution?

Super Resolution이란 저해상도 이미지를 초고해상도로 변환하는 기술을 이야기합니다. 데이터를 수집하는 과정에서는 하드웨어적인 문제나 소프트웨어적인 문제로 고품질의 데이터를 수집할 수 없을 때가 있습니다. 하지만, CNN을 통한 여러 이미지 Task(분류, 생성 등)는 이미 많은 연구들에 의해 입력 이미지로 해상도가 낮은 이미지보다 높은 이미지를 넣을 때에 성능이 개선됨이 밝혀졌습니다. 따라서, 꽤 오래전부터 이미지의 해상도를 강제로 높이려는 시도들이 많이 이루어졌고 발전되어 왔습니다. 하지만, 단순히 이미지의 사이즈를 키우는 것은 마치 마우스로 줌을 하면 이미지의 해상도가 깨지는 것과 같이 오히려 성능에 악영향을 끼칩니다. 즉, CNN 모델의 성능을 개선시키기 위해서는 해상도는 높이되 깨지는 현상은 막아야 하는 것이죠.

 

Super Resolution은 원본 이미지의 특성을 최대한 유지하면서(=해상도가 깨지지 않게 유지하면서) 초고해상도 이미지로 복원하는 기술입니다. 하지만, 이는 ill-posed problem 으로 매우 어려운 작업 중 하나입니다. ill-posed problem이란, 유일한 해가 존재하지 않는 문제를 이야기합니다. 예를 들어, 아래 그림처럼 HR1, HR2, HR3가 모델의 예측 결과라고 한다면, 이 중 어떤 결과가 잘 복원되었다고 할 수 있는지가 명확하지 않다는 것이죠. 따라서 많은 평가 지표들 (예: PSNR, SSIM 등)이 나왔지만, 이런 수치적인 평가 지표와 Perceptual 평가(지각적 평가) 지표가 서로 다른 결과를 보여주기에 평가가 어렵다는 단점이 있습니다.

배경 지식은 이정도로 마무리하고, 본격적인 프로젝트 이야기로 들어가겠습니다.

저는 이번 Super Resolution을 구현할 때에 총 3가지 기법(SRCNN, FSRCNN, SRGAN)을 적용해 볼 것입니다. 이 3가지 기법을 선택한 이유는 아래와 같습니다.

 

  1. SRCNN: Super Resolution 작업에 처음으로 딥러닝을 적용한 기법입니다. 즉, 우리가 앞으로 배울 많은 SR 기술들은 이 SRCNN에서 발전되어 만들어진 기술들입니다. 따라서, SR 기술 전반의 이해를 위하여 선택했습니다.
  2. FSRCNN: SRCNN의 발전된 기술 중 하나로, SRCNN을 구현한 결과를 확인 후 성능이 기대에 미치지 않아 발전된 기법을 구현해 보며 어떤 점이 개선되었고, 결과는 어떠한지 비교해 보려 선택했습니다.
  3. SRGAN: SRGAN은 GAN 기술을 베이스로 두고 있으며, 생성자(Generator)와 판별자(Descriminator)를 각각 따로 학습시키며, Perceptual loss라는 새로운 평가 지표가 제안됩니다. 기존 SR 기술보다 시각적으로 더 나은 결과를 보여주며 기존 loss의 문제점을 공부하기 위해 선택했습니다.

Dataset (데이콘 사이트에서 다운로드)

Dataset Info.

  • train [폴더]
  • 학습용 64x64 저해상도 조류 이미지 15,834장
  • TRAIN_00000.jpg ~ TRAIN_15833.jpg
  •  
  • test [폴더]
  • 평가용 64x64 저해상도 조류 이미 6,786장
  • TEST_00000.jpg ~ TEST_06785.jpg
  •  
  • upscale_train [폴더]
  • 학습용 저해상도 조류 이미지를 x4 Upscale 한 PNG 이미지 (256X256)
  •  
  • train.csv [파일]
  • img_path : 저해상도 조류 이미지 파일 경로
  • upscale_img_path : Upscale된 조류 이미지 파일 경로
  • label : 조류의 종 (Target)
  •  
  • test.csv [파일]
  • ID : 샘플 고유 ID
  • img_path : 저해상도 조류 이미지 파일 경로
  • label 없음

데이터 구성은 위와 같으며, 64x64 이미지를 256x256으로 x4 Upscale 복원하되 최대한 원본 이미지가 깨지지 않도록 유지하게 만드는 것이 목표입니다. train.csv에 train파일에 대한 label이 포함되어 있어 supervised learning으로 SRCNN, SRGAN 모델 모두 사용 가능함을 알 수 있었습니다.

 

Data Load

이번에 작업할 환경은 Google Colab입니다. Colab 환경에서 수행하기 위해서는 Google Drive에 데이터를 올려주어야 합니다. 그리고 gdrive와 colab을 연결해서 데이터를 사용할 것입니다. 먼저, train과 upscale_train의 데이터가 각각 15,834장이고, test 데이터가 6,786장으로 용량이 매우 큽니다. 이걸 그대로 gdrive에 옮기려면 시간도 오래 걸리고 메모리도 많이 차지하겠죠. 그래서, zip파일 그대로 gdrive에 옮겨주고 colab환경에서 unzip 하여 사용하겠습니다.

 

1) gdrive에 옮기기: Google Drive에 접속하여 Drag & Drop을 통해 zip파일을 올려줍시다.

 

2) 공유 환경 변경: colab을 통해 데이터를 다운받을 때에는 다운로드 받을 파일의 id가 필요하며, 공유 환경을 "링크가 있는 모든 사용자"로 변경해 주어야 다운로드가 가능합니다.

 

공유하고자 하는 파일 오른쪽 클릭 - 공유 - 일반 액세스 - 링크가 있는 모든 사용자로 변경 - 링크 복사


복사된 링크에서 블러 처리된 부분 복사해놓기 (이게 파일 id입니다.)

 

이렇게 하면, 기본적인 세팅은 끝났습니다. 이제 Colab 환경으로 접속하여 zip 파일을 다운로드 받고, unzip 합시다.

!pip install --upgrade --no-cache-dir gdown

import gdown
import zipfile
import os
file_id = '위에서 복사한 파일 id'
output = 'open.zip'

if not os.path.isdir('open'):
    gdown.download(id=file_id, output=output, quiet=False)

    # 파일이 정상적으로 다운로드되었는지 확인
    if os.path.isfile(output):
        print(f"'{output}' 파일이 정상적으로 다운로드되었습니다.")
    else:
        print(f"'{output}' 파일 다운로드에 실패했습니다.")

    # ZIP 파일인지 확인
    try:
        with zipfile.ZipFile(output, 'r') as zip_ref:
            zip_ref.extractall('open')
            print(f"'{output}' 파일이 정상적으로 압축 해제되었습니다.")
    except zipfile.BadZipFile:
        print(f"'{output}' 파일이 ZIP 파일이 아니거나 손상되었습니다.")

 

위 코드 실행 시 아래와 같이 open이라는 이름의 파일이 생기며 데이터를 사용할 수 있게 됩니다.

EDA

데이터의 분포부터 살펴보겠습니다. 특정 라벨의 빈도수가 너무 적거나 너무 많다면, 데이터 불균형 문제를 고려해야 하기 때문에 train.csv를 통해 확인합니다.

# 데이터프레임 로드
df_train = pd.read_csv('/content/open/open/train.csv')
df_train['label'] = df_train['label'].map(lambda x: x.replace(' ', '_'))

label_counts = df_train['label'].value_counts().reset_index()
label_counts.columns = ['label', 'count']

plt.figure(figsize=(10, 8))
sns.barplot(x='count', y='label', data=label_counts, palette='viridis')

plt.title('Distribution of Bird Species')
plt.xlabel('Count')
plt.ylabel('Species')

plt.show()

 

결과를 보니, Coppersmith Barbet, Indian Pitta는 상대적으로 적은 양의 데이터를 가지고 있네요. 하지만, 불균형 문제를 고려할 만큼의 차이는 나지 않습니다.

 

무작위로 25장의 이미지와 해당하는 라벨을 출력해 보겠습니다. Train 데이터라서 그런지 확실히 이미지들의 해상도가 낮은 게 느껴지네요.

SRCNN 구현

본격적으로 위 데이터를 이용하여 SRCNN을 pytorch를 이용하여 구현해 보겠습니다. SRCNN의 이론적인 설명은 이전 글에도 설명해 두었고, 추가적으로 제가 SRCNN을 공부하며 도움을 받은 블로그도 맨 아래에 첨부해 두겠습니다.

 

SRCNN을 구현하기 위해 총 두 가지의 시도를 했습니다.

 

1) 논문을 토대로 구현한 코드를 카피하여 우리의 목적으로 변환 후 사용

 

2) github에 올라온 pytorch 코드와 사전 학습된 가중치를 이용

 

결론부터 말씀드리면, 저는 2번 방법을 채택했고, 사전 학습된 가중치에 fine-tuning을 하여 우리 데이터셋에 맞도록 모델을 학습시키는 방법으로 진행하였습니다. 1번 방법을 포기한 이유는 아래와 같습니다.

 

1) GPU의 한계: 이번 프로젝트를 위해 colab pro를 구독하여 사용했습니다. 그러나, SRCNN 자체의 모델은 매우 단순하더라도 데이터가 매우 크고, 학습시키는 과정에서 많은 RAM을 사용하여 컴퓨팅 단위가 버티질 못했습니다.

 

2) 성능의 한계: 논문에서는 ImageNet 데이터셋도 이용하여 SRCNN을 학습시켰지만, 제안한 결과는 T91 데이터셋으로 91장의 이미지를 사용했습니다. 많은 구현 코드들에서도 같은 T91 데이터셋을 이용하여 사용했고요. 그러나, 저희는 15,834장의 데이터를 사용하다 보니 GPU가 모두 소진되었고, 훈련 데이터의 91장만 사용해 보니 성능이 기대에 미치지 못했습니다.

 

다만, 1번 방법을 시도하며 SRCNN에 학습 방식을 이해하는 데에 정말 많은 도움을 받아서, 2번 방법을 진행하면서 github에 올라온 코드를 한 줄씩 읽어가며 이해하기 수월했습니다. 저와 같은 프로젝트를 진행하시는 분들도 2번 방법을 채택하더라도 1번 방법을 거치는 것을 추천드립니다.

 

이 글에는 1번 방법에 대한 이야기를 해보려 합니다. 2번 방법은 다음 포스팅에서 이어 하겠습니다.

논문을 토대로 구현한 코드 해석

먼저, 코드는 제가 직접 구현한 것이 아닌 [케빈의 IT/코딩 세상]이라는 블로거분의 포스팅을 보며 클론 코딩하였습니다. 중간에 제 데이터에 맞도록 변환한 것은 있으나 코드의 전체적인 내용은 이 글에서 참고한 것임을 밝힙니다.

 

먼저, 하이퍼파라미터와 데이터 경로부터 지정합니다. 하이퍼파라미터는 논문에 나와있는 것을 그대로 사용하였습니다.

n1, n2, n3 = 128, 64, 3 #필터 개수
f1, f2, f3 = 9, 3, 5 #필터 크기
upscale_factor = 3

input_size = 33
output_size = input_size - f1 - f2 - f3 + 3
pad = abs(input_size - output_size) // 2
stride = 14

path = '/content/open/open/upscale_train'
save_path = '/content/open/open/SRCNN_200epochs.h5'

callbacks = [keras.callbacks.ModelCheckpoint(
      filepath=save_path,
      save_best_only=True,
      monitor="loss")]
batch_size = 128

 

이미지를 한 장만 출력해 봅시다. cv2 라이브러리를 사용하며 BGR에서 RGB채널로 변경하여 읽어줍니다.

path가 upscale_train으로 지정되어 있기 때문에 고해상도 사진이겠네요. 결과는 아래와 같습니다.

img_paths = glob(path + '/' + '*.png')

img = cv2.imread(img_paths[0], cv2.COLOR_BGR2RGB)
print(img.shape)
plt.imshow(img)

 

이 코드가 이해하기 어려웠는데요. 하나씩 살펴보겠습니다. 저는 RAM의 소모를 최대한 막기 위해 미니 배치 방식을 사용하였습니다. batch_img_paths에는 미니 배치에 포함되는 고해상도 이미지들의 경로가 들어있습니다. 그러면, img는 미니 배치에 포함된 이미지들을 한 장씩 가져온 이미지가 되겠네요.

 

h와 w는 mod crop 한 이미지입니다. 논문을 보면, 저해상도 이미지를 추출하는 방식으로 고해상도 이미지를 upscale_factor만큼 줄였다가 bicubic 방식을 활용하여 다시 upscale_factor만큼 키우면서 원래 사이즈로 만들어주는 방식을 사용합니다. 따라서, 원본 이미지의 h와 w가 upscael_factor의 배수가 되지 않으면 줄였다가 키우는 과정에서 소수점이 발생하게 되고 버려지는 부분이 생기게 되죠. 따라서, mod crop을 통해 img를 upscale_factor의 배수의 크기를 가지도록 변경합니다.

 

temp_input과 input_img의 과정이 위에서 언급한 것처럼 img를 upscale_factor만큼 줄였다가 bicubic 방식으로 다시 키우면서 저해상도 이미지를 추출하는 과정입니다. 여기까지 하면, 우리에게 label로 고해상도 이미지 (256X256)와 input_img로 저해상도 이미지 (256X256)가 생기게 되죠. 이제 이 input_img와 label에서 패치를 추출하여 sub_lr_img와 sub_hr_img에 넣어줄 것입니다.

 

input_size는 33, stride는 14로 imput_img에서 stride만큼 이동하면서 33X33 크기의 패치를 추출하게 됩니다. 이 과정이 정확히 Convolution layer에서 필터의 역할과 동일합니다. hr 이미지도 마찬가지로 14칸씩 건너뛰면서 패치를 추출하는데, 이번에는 output_size의 크기의 패치를 추출합니다. 이렇게 하는 이유는, SRCNN 모델의 경우 합성곱층만 통과하기 때문에 output의 크기가 input의 크기보다 작아질 수밖에 없습니다. 그리고 그렇게 나온 결과에 sub_hr_img를 이용하게 되죠. 따라서, SRCNN의 모든 층을 거치고 나온 결과와 sub_hr_img를 비교하려면, sub_hr_img의 크기는 SRCNN을 거친 output_size와 동일해야 합니다. 따라서 19X19의 사이즈를 가지는 패치를 추출합니다.

 

(지금은 이해가 어려울 수 있습니다. 아래 모델의 아키텍처를 보시면 더 이해가 편하실 겁니다.)

batch_size = 32
sub_lr_imgs = []
sub_hr_imgs = []

for batch_start in range(0, len(img_paths), batch_size):
    batch_img_paths = img_paths[batch_start:batch_start+batch_size]
    for img_path in batch_img_paths:
        img = cv2.imread(img_path)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        #mod crop: upscale_factor의 배수가 되도록 h,w를 맞춤. --> 그래야 아래에서 input_img를 만들 때 포함되지 않는 부분이 없음.
        h = img.shape[0] - np.mod(img.shape[0], upscale_factor)
        w = img.shape[1] - np.mod(img.shape[1], upscale_factor)
        img = img[:h, :w, :]

        #temp_input: upscale_factor만큼 다운샘플링. input_img: 다시 temp_input을 bicubic방식으로 upscale_factor만큼 업샘플링.
        label = img.astype('float') / 255
        temp_input = cv2.resize(label, dsize=(0,0), fx=1/upscale_factor, fy=1/upscale_factor, interpolation=cv2.INTER_AREA)
        input_img = cv2.resize(temp_input, dsize=(0,0), fx=upscale_factor, fy=upscale_factor, interpolation=cv2.INTER_CUBIC)

        #sub_image 추출 --> sub_lr_img, sub_hr_img 모두 이미지 하나 당 256장씩의 패치가 존재.
        for h in range(0, input_img.shape[0] - input_size + 1, stride):
            for w in range(0, input_img.shape[1] - input_size + 1, stride):
                sub_lr_img = input_img[h:h+input_size, w:w+input_size, :]
                sub_hr_img = label[h+pad:h+pad+output_size, w+pad:w+pad+output_size, :]

                sub_lr_imgs.append(sub_lr_img)
                sub_hr_imgs.append(sub_hr_img)

sub_lr_imgs = np.asarray(sub_lr_imgs)
sub_hr_imgs = np.asarray(sub_hr_imgs)

 

이 과정을 거치면 몇 개의 패치가 추출되는지를 확인해 보죠.

이미지 한 장 당 256장의 패치를 추출하게 되어 총 400만 장이 넘는 이미지가 추출되네요. 이러다 보니 RAM이 견디질 못하고 터지는 현상이 벌어집니다.

print(f"이미지 크기 * 패치 수: {15834 * 256}")  #4,053,504
print(f"고해상도 이미지의 패치 총 개수: {len(sub_hr_imgs)}")   #4,053,504
print(f"저해상도 이미지의 패치 총 개수: {len(sub_lr_imgs)}")   #4,053,504

#지금 문제는, 이 이미지 수가 너무 많아서 이 이미지를 모두 사용하면 ram이 터져버린다는 것.

 

간단히, 추출한 저해상도 이미지와 고해상도 이미지를 하나씩 출력하는 코드입니다.

fig,axes = plt.subplots(1,2,figsize = (5,5))
idx = random.randint(0, sub_lr_imgs.shape[0])

axes[0].imshow(sub_lr_imgs[idx])
axes[1].imshow(sub_hr_imgs[idx])

print(idx)
axes[0].set_title('lr_img')
axes[1].set_title('hr_img')

 

SRCNN 모델을 정의하겠습니다. input_shape은 우리가 위에서 추출했던 sub_lr_imgs를 넣을 것이므로 (33,33,3)으로 지정하고, 가중치는 논문에서 나온 것과 같은 GlorotNormal로 지정해 줍시다. 여기서 n1, f1,.. 은 모두 처음 지정했던 하이퍼파라미터입니다.

 

그러면, 작동 방식을 하나씩 뜯어보자면 저해상도 패치를 입력받아서 그 저해상도 패치의 특성들을 추출합니다. 그게 Convolutional layer이죠. 그리고, 두 번째 Conv 층에서는 고해상도의 패치의 차원과 맞도록 바꾸어주고, 마지막 Conv층에서는 그렇게 나온 특성들을 linear combination 해주면서 그 결과를 sub_hr_imgs에 있는 고해상도 패치와 비교하여 mse를 낮추도록 학습하게 되죠. 그러면, 저해상도 패치가 점점 고해상도 패치와 비슷하게 변해갈 것이고, 그렇게 고해상도 이미지를 생성하도록 변하게 되는 것입니다.

initializer = initializers.GlorotNormal()

SRCNN = Sequential()
SRCNN.add(Conv2D(filters = n1, kernel_size = f1, activation = 'relu', input_shape = (33,33,3), kernel_initializer = initializer, bias_initializer = 'zeros', name = 'Conv1'))
SRCNN.add(Conv2D(filters = n2, kernel_size = f2, activation = 'relu', kernel_initializer = initializer, bias_initializer = 'zeros', name = 'Conv2'))
SRCNN.add(Conv2D(filters = n3, kernel_size = f3, activation = 'linear', kernel_initializer = initializer, bias_initializer = 'zeros', name = 'Conv3'))
print(SRCNN.summary())

 

학습을 시켜보죠. 200 에폭을 진행할 것이며, 학습률은 0.0003으로 고정하여 진행합니다.

optimizer = Adam(learning_rate = 0.0003)

SRCNN.compile(optimizer = optimizer, loss = 'MSE', metrics = 'MSE')
SRCNN.fit(sub_lr_imgs, sub_hr_imgs, batch_size = batch_size, epochs = epochs, callbacks = [callbacks])

 

결과를 한 번 살펴보죠. 왼쪽부터 원본 고해상도 이미지, Bicubic interpolation으로 복원한 이미지, SRCNN을 이용한 이미지입니다. 결과가 나름 나쁘지는 않은 것 같습니다. 다만, 아직 디테일한 부분은 blur처리를 한 것과 같이 보이기도 하죠.

 

문제점

이 코드를 구현하면서 문제점은 두 가지가 있었습니다.

 

1) RAM이 터져서 우리 데이터를 모두 활용할 수 없다: 가지고 있는 데이터를 사용할 수 없다는 것은 매우 큰 제약입니다. 마치 시험 범위가 100장인데 겨우 10장만 보고 시험을 보는 느낌이죠. 당연히 성능이 잘 나올 리가 없습니다. (그럼에도, 91장만 사용한 거 치고는 결과가 꽤 잘 나오긴 합니다.)

 

2) 구조적 한계: 이 코드는 고해상도 이미지에서 저해상도 이미지를 추출하고 있습니다. 즉, 입력 이미지 자체가 고해상도 이미지여야 한다는 것이죠. 하지만, 우리가 가지고 있는 Test data는 64X64의 저해상도 이미지입니다. 이 이미지를 그대로 넣으면, 학습 알고리즘에 따라 저해상도 이미지를 복원하려 할 것입니다. 즉, 저해상도를 고해상도로 변환하는 것이 아닌 저해상도를 저해상도로 복원하려는 시도를 할 것입니다.

 

위 두 가지 이유로 SRCNN을 논문 내용 그대로 구현하는 것은 멈추었습니다. 다음 포스팅에서는 Pytorch를 이용한 github 코드를 살펴보며, 극복한 과정을 살펴보겠습니다.

 

 

Insight
: SRCNN은 SR분야에 처음으로 딥러닝이 적용된 기술이며, 이를 개선한 모델들이 SOTA 모델들이기 때문에 기초를 배울 필요가 있었다. 내가 생각하는 모델의 구조를 이해하기 가장 빠른 방법은 클론 코딩인 것 같다. 다만, 무작정 따라 적는 것이 아닌 한 줄 한 줄을 이해하며 넘어가야 한다. 시간은 오래 걸리지만, 이번 클론 코딩을 통해 논문으로는 이해가 되지 않았던 개념들이 확실히 잡히게 되었다. 다만, 아직 코드의 개선이 필요해 보인다. 내가 가진 데이터는 약 15,000장 정도인데, 그 중 91장만 사용하더라도 패치가 약 400만장이 만들어짐을 확인했다. 즉, 91장만 사용하더라도 모델이 받아들여야 하 input image는 400만장이 되는 것이다. 따라서, 모델이 매우 무거우며 동시에 RAM 공간 부족 문제에 부딪혔다. 학습의 과정을 END-TO-END 방식이 아니라 FINE-TUNING으로 바꾸면 어떨까. 기존의 ImageNet dataset으로 학습해둔 가중치가 github에 올라와있음을 확인했다. 이 가중치를 입힌 모델에 우리 데이터를 FINE-TUNING하여 사용해보는 것이 다음 목적이다. FINE-TUNING을 사용하면, 적은 에폭으로도 학습이 잘 되기 때문에 효과적일 것이라고 기대는 하지만, 걱정되는 것은 우리가 가진 데이터의 반의 반의 반의 반의 반도 사용하지 않는다는 점이다. 학습 결과를 살펴보며, 결정해봐야겠다. 일단 해봐야지.

마치며..

프로젝트를 할 때마다 느끼지만, 배울 것이 정말 많은 것 같습니다. 어떻게 보면, 배우는 것이 많으니 좋기도 하지만, 마음대로 흘러가지 않을 때는 지치기도 합니다. 사실, 지금 하고 있는 이 프로젝트는 Dacon에서 이미 끝난 공모전이기 때문에 공유된 코드들을 보면서 해도 됩니다. 다만, SR기술을 이용한 코드가 없더라고요. 그래서 왜 SR 기술을 사용하지 않는 것인지도 알아보고, 최근 범죄 수사에도 사용이 되고 있는 SR 기술에 관심이 많아져 스터디해보려 합니다.

 

저와 같은 프로젝트를 준비하시는 분들에게 도움이 되었으면 합니다. 오늘도 읽어주셔서 감사합니다.

References
https://kevinitcoding.tistory.com/entry/%EB%85%BC%EB%AC%B8-%EA%B5%AC%ED%98%84-SRCNNby-Keras%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-Super-Resolution
 

[논문 구현] SRCNN(by Keras)을 활용한 Super Resolution

저번 시간에는 SRCNN 논문을 리뷰해 봤습니다. 이번 딥러닝 프레임 워크 중, Keras를 활용하여 SRCNN을 구현해 보도록 하겠습니다. 이 코드는 앞에서 제가 작성한 논문 리뷰 내용을 기반으로 작성했

kevinitcoding.tistory.com

https://ieeexplore.ieee.org/abstract/document/7115171/

반응형