Unix

Keras + Tensorflow/Theano, 3. 나만의 이미지 입력 만들기

ForceCore 2016. 5. 11. 13:52

사실 이게 제일 어려워서 (?) 내가 이 글 시리즈를 쓴 것이다. 알고나면 쉬운데 모르면 힘들다.


논문을 쓰는거라면 이미 있는 벤치마크를 받아 쓰면 된다. 그러면 딱히 데이터 형식이 어떻게 되어있는지 고민할 필요는 없을텐데... 나는 내 개인적인 관심사를 위해 Keras를 설치한 것이라, 내가 수집한 데이터를 입력으로 줘야 한다. 문제는, keras/theano를 처음 써봐서 그걸 어떻게 하는건지 모르겠다는 것!


https://github.com/jocicmarko/kaggle-dsb2-keras


여기에서 힌트를 얻었다.


numpy.array

데이터형을 이미지로 쓴다.


예제코드를 보면

    (X_train, y_train), (X_test, y_test) = mnist.load_data()


벤치마크 로딩이 이렇게 나와있을 것이다.

X_train, y_train이 뭔지도 몰랐다 처음엔.

train? 기차? 기차처럼 데이터가 주욱 길게 나열된거라 그런가? 라고 생각했는데 training set, test set할 때의 train이었다 -_-


print( type( X_train ) )

이거는 numpy.array 라고 나오고 (numpy 수학 라이브러리의 다차원 배열... 텐서? 2D인 행렬보다 더 고차원인거)


print( X_train.shape ): array의 속성인데, X_train이 몇차원으로 생긴 놈인지 보여주는 것이다.

(23, 3, 64, 64) 이런 식의 출력이 나올 것이다. 23개의 이미지가 있는데, 3채널이고, 각각의 R, G, B채널에 저장된 이미지는 64x64이다. (mnist가 이런건 아니고 내가 준비한 이미지가 이렇다... 아직 이미지 입력 자체를 코딩하기 위해 대량의 데이터를 다 넣지는 않은 상태)


img0 = X_train[ 0 ]

img1 = X_train[ 1 ]

...

이렇게 가면 0번째 이미지, 1번째 이미지 등을 얻을 수 있다.

img[0], img[1], img[2] 를 접근하면 R, G, B채널의 이미지를 얻는다.

보통 numpy.misc.imread를 하면 3x64x64 순서가 아니라 64x64x3으로 나올 것이다.

뭐자면 64x64개의 칸이 준비되어 있는데, 각 칸 안에 RGB색을 박았단 뜻이다.

3x64x64의 3장의 사진인데 하나는 빨간색 사진, 중간 것은 녹색 사진, 마지막은 파란 사진 인것과 대비된다. 조심해서 다룰 것.



그래서 필자가 짠 특정 폴더의 이미지 읽는 코드는 아래와 같다.


def crop_resize(img):
    """
    Crop center and resize.

    :param img: image to be cropped and resized.
    """
    #if img.shape[0] < img.shape[1]:
    #    img = img.T
    # Why did they do this?

    # we crop image from center
    short_edge = min(img.shape[:2])
    #print( img.shape )
    yy = int((img.shape[0] - short_edge) / 2)
    xx = int((img.shape[1] - short_edge) / 2)
    assert yy >= 0 and xx >= 0
    assert yy == 0 or xx == 0 # one of 'em is zero
    #print( xx, yy )
    #print( xx+short_edge, yy+short_edge )

    crop_img = img[yy: yy + short_edge, xx: xx + short_edge]
    #crop_img = img[xx: xx + short_edge, yy: yy + short_edge]
    img = crop_img
    img = imresize(img, img_shape)
    return img


def load_images( from_dir, suffix ) :
    """
    Load xxx images...

    :param from_dir: directory with images (train or validate)
    """

    print( "Processing dir", from_dir )
    CNT = 23 # you set this manually.

    ids = []
    images = []

    for i in range( CNT ) :
        lbl = str( i+1 ) + suffix
        # serial number in 1 ... CNT
        fname = lbl + '.png'
        fname = os.path.join( from_dir, fname )

        img = misc.imread( fname, mode='RGB' )
        img = img.astype( float )
        img /= np.max( img ) # scale to [0,1]
        img = crop_resize( img ) # do 64x64 crop

        # Forced RGB loading will ensure 3 channels.
        if not img.shape == (64,64,3) :
            print( img.shape )
            assert img.shape == (64,64,3)

        img = make_rgb_slices( img )
        images.append( img )
        ids.append( lbl )

    # convert from python list to pure numpy array.
    images = np.array( images )


    return ids, images




def make_rgb_slices( img ) :
    assert img.shape == (64,64,3)
    r = img[:,:,0]
    g = img[:,:,1]
    b = img[:,:,2]
    return np.array( [ r, g, b ] )


# np.swapaxes can do such job also

#    return np.swapaxes(np.swapaxes(t, 1, 2), 2, 3)


이러면 된다.


이후에 필자는 또...

이미지 --> 라벨

이런 관계의 평범한 fitting이 아니라

이미지 --> 이미지의 fitting을 해야 했는데, 그건 쉽다. (그런 예를 들면, image super-resolution이 있다. 이미지를 지능적으로 계산현상 없이 확대하는거...)


https://github.com/nanopony/keras-convautoencoder/blob/master/conv_autoencoder.py

여기에서 볼 수 있듯이, fit() 함수에 그냥 돌리면 된다. 라벨대신 이미지를 특별한 처리 없이 넣어주면 됨.