본문 바로가기

Deep Learning/Keras & Tensorflow

ImgaeDataGenerator.flow_from_directory을 이용해 이미지 증식하는 방법

320x100
320x100

 

 Data augmentation은 Network의 robustness를 높이기 위해 거의 default로 적용하는 방법이다. 케라스에서는 ImageDataGenerator 클래스의 몇 가지 클래스 함수들로 이미지 로드 뿐만 아니라 augmentation을 지원한다.

  • flow( )
  • flow_from_directory( )
  • flow_from_dataframe( )

 이번 글에선 flow_from_directory( ) 함수를 사용해서 augmentation을 하는 방법에 대해 정리하고자 한다. 그러기 위해선 일단 사용할 이미지가 필요하다. 저작권 문제에서 자유로운 https://pixabay.com/ 에서 마음에 드는 고양이와 강아지 사진을 각각 2장씩 골라 아래와 같은 폴더 구조로 저장하자. 여기서 이렇게 폴더명에 맞춰서 이미지를 저장하는 이유는 이미지를 불러올 때 폴더명에 맞춰서 자동으로 labelling하는 flow_from_directory( ) 함수의 기능을 활용하기 위함이다.

 

     example  folder

            └  dog folder : dog1.jpg / dog2.jpg

            └  cat  folder : cat1.jpg  / cat2.jpg

 

 


 

이제 셋팅은 끝났으니 코드단으로 넘어가서 사용법을 알아보도록 하자.

import tensorflow as tf
import numpy as np
import cv2
 
np.random.seed(15)
 
path = 'example'
 
generator = tf.keras.preprocessing.image.ImageDataGenerator(
    rotation_range = 20,
    width_shift_range = 0.2,
    height_shift_range = 0.2,
    rescale = 1. / 255)
 
batch_size = 4
iterations = 5
images = []
  • [ Line 5 ]
    Keras의 Random Seed 설정은 np.random.seed( )를 통해 해줄 수 있다.
    재현성 잡힌 Image Load & Augmentation 결과를 얻고 싶다면 np.random.seed( )를 설정해주도록 하자.
  • [ Line 9 ~ 13 ]
    ImageDataGenerator 인스턴스를 generator라는 변수명으로 생성하자.
    생성시에 파라미터를 설정하면 어떻게 augmentation를 진행할지 지정할 수 있다.
  • [ Line 15 ~ 16 ]
    - example folder에 있는 이미지 4장을 한 번에 읽어들이기 위해 batch_size = 4 로 설정
    - augmentatoin을 5번 적용하기 위해 iterations = 5 로 설정

 

이렇게 생성한 generator 인스턴스의 flow_from_directory( ) 함수를 사용하는 방식에는 두 가지가 있다.

  1. next( ) 함수 사용
  2. for 문에서 flow_from_directory( ) 호출

각 케이스 별 사용법에 대해 자세히 알아보도록 하자.

 

 


그 전에 광고 하나 보고 가자!

728x90
728x90

 


 

 

(1) next( ) 함수 사용 방법

obj = generator.flow_from_directory(
    path,
    target_size = (150, 150),
    batch_size = batch_size,
    class_mode = 'binary')


for i in enumerate(range(iterations)):
    img, label = obj.next()
    n_img = len(label)
    
    base = cv2.cvtColor(img[0], cv2.COLOR_RGB2BGR)  # keras는 RGB, openCV는 BGR이라 변경함
    for idx in range(n_img - 1):
        img2 = cv2.cvtColor(img[idx + 1], cv2.COLOR_RGB2BGR)
        base = np.hstack((base, img2))
    images.append(base)

img = images[0]
for idx in range(len(images) - 1):
    img = np.vstack((img, images[idx + 1]))
cv2.imshow('result', img)
  • [ Line 5 ]
    class_mode는 어떤 방식으로 폴더명에 따른 labelling을 진행할 수 있는 파라미터이다.
    'binary'로 설정하면 0 or 1로 labelling이 진행된다.
  • [ Line 9 ]
    next( ) 함수 사용 방법을 제대로 보여주는 코드 한 줄이다.
    for문에서 obj.next( )를 한 번 호출할 때 마다 
       (1) obj는 설정된 경로에서
       (2) batch_size에 맞춰서
       (3) 이미지를 target_size로 resizing 한 다음에
       (4) 폴더명을 기반으로 'binary' 방식에 맞춰 labelling까지 진행해서 이미지를 불러온다.
    따라서 여기에선 obj.next( ) 한 번 호출하면
    4장의 이미지를 150 x 150 size로 label 정보와 함께 볼러오게 된다.
 
최종적으로 augmentation 결과가 저장된 image를 띄워보면 다음 사진과 같다.

 

( iterations x batch_size )의 형태로 사진이 합성되어 있는 걸 확인할 수 있다. 4개의 이미지를 5번 이미지 증식해 불러왔기 때문이다. 또한 사진을 보시면 원본 이미지에 비해 회전, x축 이동, y축 이동이 된 형태로 변형되어 생성됐다는 걸 알 수 있다. 추가로 위에서 random_seed를 고정해뒀기 때문에 여러번 실행을 반복해도 결과 이미지에는 변함 없는 것을 확인할 수 있다.

 

(2) for문 선언 과정에서 flow_from_directory( ) 바로 사용

obj = generator.flow_from_directory(
    path,
    target_size = (150, 150),
    batch_size = batch_size,
    class_mode = 'binary')

for i, (img, label) in enumerate(obj):
    n_img = len(label)

    base = cv2.cvtColor(img[0], cv2.COLOR_RGB2BGR)
    for idx in range(n_img - 1):
        img2 = cv2.cvtColor(img[idx + 1], cv2.COLOR_RGB2BGR)
        base = np.hstack((base, img2))
    images.append(base)
    
    if i is iterations - 1:
        break

img = images[0]
for idx in range(len(images) - 1):
    img = np.vstack((img, images[idx + 1]))
cv2.imshow('result', img)
  • [ Line 7 ]
    첫 번째 방법과 큰 차이는 없지만 거의 유일하게 다른 한 줄이다.
    obj.next( )를 직접 호출했던 첫 번째 방법과는 다르게 for문 선언 라인에서 바로 obj를 사용했다.
    for문이 자동으로 obj.next( )를 호출한다는 이용한 방식이다.
  • [ Line 16, 17 ]
    하지만 이 방법을 쓰면 for문의 반복 횟수를 직접적으로 제한할 길이 막혀서
    해당 코드를 추가해서 반복을 제한했다.
  • 마찬가지로 이미지를 띄워서 결과를 확인해보면 위와 같은 결과 이미지가 생성되는 것을 확인할 수 있다.
 
 

 

 

지금까지 flow_from_directory를 통해 이미지를 불러오고 그와 동시에 augmentation 적용까지 해버리는 방법에 대해 알아 봤다. 설명이 틀린 곳이 있을 수 있으니.. 의심 되는 부분은 댓글로 알려주시면 감사하겠습니다.