オリジナルのデータセットを読み込むための準備

Deep Learningをやっていると、サンプルのデータセットだけではなく、やがて自分で用意したデータセットを読み込みたくなりますよね。
画像を集めるのは何とかなるけど、その後どうしたらいいの？　ということで、理解しやすそうな手順に沿ってメモを残してみました。

# ファイルを読み込む
ファイルを読み込むには、TensorFlowの関数を使用します。
ずばり「tf.read_file()」一択。

使い方はこんな感じです。

```python
image_r = tf.read_file(fname)
```

fnameは読み込みたいファイル名です。
日本語が入っている場合は、UTF-8になってないとエラーになります。
（たぶん文字コードは変えられる、と思う ^^;）

# 画像を読み込む
画像を読み込むには、TensorFlowの関数を使用します。
とりあえず、PNGとJPEGに対応しています。

- tf.image.decode_jpeg
- tf.image.decode_png

さらに、上記2種類のどれでも読み込めるという便利な関数も用意されています。

- tf.image.decode_image

ただし、BMPとかTIFとかは読めません。

使い方はこんな感じです。

```python
image_r = tf.read_file(fname)
image = tf.image.decode_image(image_r, channels=3)
```

流れとしては、まずファイルを読み込み、フォーマットに従いデコードします。
読み込んだ結果は（縦、横、チャンネル）の配列になっています。
具体的に説明すると、2x2xRGBの画像の場合、
　[[[R,G,B]　# (x,y) = (0,0) 左上
　[R,G,B]]　# (x,y) = (1,0) 右上
　[[R,G,B]　# (x,y) = (0,1) 左下
　[R,G,B]]]　# (x,y) = (1,1) 右下
の順番になります。

【余談】
実はGIFも読み込めるのですが、デコード後の結果が（フレーム、縦、横、チャンネル）とアニメーションGIF用になっているようです。
少しだけですが他とデータが異なるので、GIFは読めないと言い聞かせることにします（爆）

# 読み込むファイルを指定する
実際にデータセットを読み込む場合は、おそらくディレクトリをクロールするか、定義ファイルを読み込むことになります。
ここではとりあえず、定義ファイルを読み込む手順を記述します。

定義ファイルはテキストファイル（CSVとか）で記述されているとします。
例えばこんな感じ。

```text
c:\work\image\image1.png
c:\work\image\image2.png
c:\work\image\image3.png
```

Deep Learningなので、ラベル番号なんかを付けてみるとこうなります。

```text
c:\work\image0\image1.png, 0
c:\work\image0\image2.png, 0
c:\work\image1\image3.png, 1
```

このCSVを読み込むには、やはりTensorFlowの関数を使用します。
（何でもあって、便利便利！）

まず、1行づつ順番に取り出すために、キューを用意します。この関数が「tf.train.string_input_producer()」です。
次に、テキストファイルを1行づつ読み込むために「tf.TextLineReader」というクラスが用意されており、こいつの「read()」関数に先ほどのキューを指定することで、実際に1行分のデータを読み込むようになります。
その後、CSVのフォーマットに従いパースして、今回はファイル名とラベルに分解します。

コードはこんな感じ。

```python
fname_queue = tf.train.string_input_producer([csvfile])
reader = tf.TextLineReader()
key, val = reader.read(fname_queue)
fname, label = tf.decode_csv(val, [["aa"], [1]])
```

csvfileは読み込むCSVファイル名、keyにはCSVファイル名と行番号、valにはその行の文字列、fnameに1カラム目（ファイル名）が入り、labelに2カラム目（ラベル番号）が入ります。
このfnameを使って、画像データを読み込んでいくことになります。

キューのところがいまいち理解しにくいのですが、一気にCSVを読み込むのではなく、くるくる回りながらその都度1行づつ読んでいくイメージができれば、なんとなくつかめるような気がします。
（keyやvalueにアクセスした段階で、次の行を読み込むようにカウントアップされる模様）

ということで、このCSVのおかげで、画像ファイルがどういう構造で保存されていてもそれなりに対応できるようになります。
ただ、このCSVファイルを作るのが面倒なので、いずれディレクトリをクロールしてCSVを作成するコードも作成したいと思っています。

# 実行する
実はここまでのコードだけでは、実際にはファイルを読み込んだりしないのです。
TensorFlowのポイントなのですが、先にグラフを構築し、後から実行する流れになっています。
つまり今回も、今までの部分はグラフ構築の部分で、実行の部分を書いていませんでした。

よって、実際に実行するためには、以下のようなコードが必要になります。

```python
sess = tf.Session()
init = tf.initialize_all_variables()
sess.run(init)
tf.train.start_queue_runners(sess)
x = sess.run(image)
```

セッションを作成し、初期化を行い、キューを開始し、実行します。
この「tf.train.start_queue_runners()」を忘れると、キューが動かず、1ファイルも読み込まれませんのでご注意ください。（動かないだけでなく、固まって、止めることも不可能に．．．）
最終結果であるxは読み込んだ画像データ（全ファイル数分）になっています。

# おまけ
全部をまとめて、実際に試したコードはこちら。

```python
import sys
import tensorflow as tf

def read_csv(csvfile):
    fname_queue = tf.train.string_input_producer([csvfile])
    reader = tf.TextLineReader()
    key, val = reader.read(fname_queue)
    fname, label = tf.decode_csv(val, [["aa"], [1]])
    return read_img(fname)

def read_img(fname):
    img_r = tf.read_file(fname)
    return tf.image.decode_image(img_r, channels=3)

def main():
    argv = sys.argv
    argc = len(argv)
    if (argc < 2):
        print('Usage: python %s csvfile' %argv[0])
        quit()

    image = read_csv(argv[1])

    sess = tf.Session()
    init = tf.initialize_all_variables()
    sess.run(init)
    tf.train.start_queue_runners(sess)
    x = sess.run(image)

    print(x)

if __name__ == '__main__':
    main()
```

CSVファイル名は引数で渡すようにしています。

# おまけ（２）
Deep Learningで処理するには、CSVに書いたファイル全部を取得してしまってからでは大変なので、何ファイルか毎に取得するように修正する必要があると思っています。
また、CSVファイルに書いた順番通りに持ってこられても困るので、shuffleする仕組みも入れる必要があります。
（tf.train.string_input_producer()にありそうです）
さらにさらに、取得した画像を、反転したり回転したり動かしたり、何らかの処理を行って画像数の水増しをしたくなるような気もします。

この辺りを、次に調べたいなと思っています。