本記事では、 keres ライブラリの ImageDaraGenerator を使用して、画像データを加工した上で画像の数を増やす方法について解説していきます。
なぜ画像を加工するのか?
まず、なぜ画像を加工して増やさなければいけないのかについて考えます。
機械学習において特に重要なのは、構築したモデルに対して学習を行わせるフェーズです。
そこではモデルに大量の画像を読み込ませ、そこから対象の特徴量を抽出する必要があります。
ここで読み込ませる画像が少ないと、対象を識別するための特徴量を適切に定義出来なくなる可能性があります。
しかし現実問題、何百、何千枚もの画像を個人レベルで用意するのは難しいことです。
そこで考え出されたのが、データ拡張という手法です。
データ拡張とは、画像データに加工を加えた上で複製することです。
このようにして生成された画像は、元のデータと本質は同じでありながらも異なる特徴量をもつデータへと変質するためモデルに訓練用データとして渡すことができ、結果的に少量のデータでもモデルに学習を行わせることを可能にします。
そのような理由が、画像の加工及び複製へのモチベーションとなっています。
keras とは?
keras は、pythonで作成された機械学習用のライブラリです。
シンプルなモジュール構成となっており、モデル構築なども簡単に行えるよう設計されています。
2つの実装が存在しますが、今回使用するのは、 TensorFlow に統合されたkerasです。
ImageDataGenerator とは?
kerasライブラリに用意された、データ拡張( Data Augmentation ) 用のクラスです。
画像データの角度を変更したり、移動させたり、回転させたりなどの処理を加えることで、少量の画像データから大量の亜種画像データを生成することができます。
実装
今回はピンポイントの技術解説なので、早速実装に入っていきます。
先に述べたように、ImageDaraGenerator では画像に対して様々の加工処理を施すことができます。
が、最初から種々の加工を施してしまっては変化がよく分からないことになってしまうので、まずは1点だけ加工する実装を行った後、多数の点を加工する実装とに分割したいと思います。
今回使用する画像データは、下記の1枚です。
亀万年の丸眼鏡。とっても良いですね。
実装A. 1枚の画像に対して回転処理を加え、9枚の画像を生成する
タイトルの通りです。
大きな流れは次の通りです。
- 画像の読み込みと整形
- ジェネレータの定義
- ジェネレータの生成、画像の生成、生成された画像の保存
- 生成されたデータの表示
注意するところとしては、ジェネレータは4次元の配列形式でしか画像データを受け取ってくれない、というところでしょうか。
1枚の画像データを numpy の array() で配列化すると、 (高さ、幅、チャンネル) の3次元配列を返却します。
複数の画像であればこの3次元データを束ねることで4次元になりますが、1枚しか無い場合は、 numpy の newaxis() メソッドで次元を追加してあげましょう。
これにより、(1、高さ、幅、チャンネル) の4次元配列となり、ImageDataGenerator に渡すことの出来るデータになります。( # 2 の部分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
import os import numpy as np import matplotlib.pyplot as plt from tensorflow.python.keras.preprocessing import image from tensorflow.python.keras.preprocessing.image import ImageDataGenerator # 0. 前準備 path = "pic/glasses/" ## 読み込み画像へのpath pic_name = "round3" ## 画像の名称 jpg = ".jpg" ## 拡張子 save_path = "pic/glasses/extendDatas/" ## 生成された画像の格納path os.makedirs(save_path, exist_ok=True) ## 格納先ディレクトリが存在しなければ作成する # 1-1. テスト用画像の読み込み img = image.load_img(path + pic_name + jpg) # 1-2. 画像の配列化 img = np.array(img) # 2. 配列を四次元に整形 print("整形前の配列shape : {}".format(img.shape)) print("( 四次元配列でないとジェネレータに渡せないので、np.newaxisで次元追加 )") img = img[np.newaxis] print("追加後shape : {}".format(img.shape)) # 3. ジェネレータの定義 # データ拡張のパターンを指定する glasses_gen = ImageDataGenerator(rotation_range = 50) # 4. ジェネレータを生成。 gen = glasses_gen.flow(img, batch_size = 1, ## 起動バッチ数の指定。今回は画像1枚なので1 save_to_dir = save_path, ## 保存先ディレクトリのpath save_format = "jpg" ## 保存形式の指定 ) plt.figure(figsize=(10,8)) # 表示画像サイズ # 5. 9枚の画像を生成する for i in range(9): # ジェネレータにおけるイテレータを進める batches = next(gen) # 1次元目は次元合わせだったので読み飛ばす。 # 画像として表示するために、floatからuint8に変換する。 g_img = batches[0].astype(np.uint8) # matplotlib.pyplot.subplot # ... 一つの図の中に複数のイメージを格納したいときに用いる。 # 引数は前から、行数、列数、何番目のプロットとして表示するかの指定。 # 下の記述で言えば、 3x3 の表示枠を一つの図中に用意し、順番に表示データを当て込んでいる。 plt.subplot(3, 3, i+1) plt.imshow(g_img) # matplotlib.pyplot.axis # ... 座標軸を表示するかどうかを指定する。off 指定で表示されなくなる。 plt.axis('off') # 6. 表示 plt.show() |
1 2 3 |
整形前の配列shape : (600, 900, 3) ( 四次元配列でないとジェネレータに渡せないので、np.newaxisで次元追加 ) 追加後shape : (1, 600, 900, 3) |
このように、1枚の画像から9枚の画像が生成されました。
似たような画像が多いですが、よく見てみると画像の角度が微妙に異なるのがわかります。
これは加工条件として “回転域を -50° ~50°” に設定したためであり、その域内でランダムに画像が加工された結果です。
ランダムに、というのが微妙にポイントです。
実装B. 1枚の画像に対して複数の変更処理を加え、9枚の画像を生成する
では加工条件を複数追加してみます。
先ほどのプログラムのジェネレータ定義部分を次のように書き換えた上で実行してみます。
加工ポイントはインコードで記述しています。
1 2 3 4 5 6 7 8 9 10 |
# 3. ジェネレータの定義 # データ拡張のパターンを指定する glasses_gen = ImageDataGenerator(rotation_range = 50, ## 回転角度の変更域の指定 width_shift_range = 0.3, ## 水平方向への移動域の指定 height_shift_range = 0.3, ## 垂直方向への移動域の指定 shear_range = 0.1, ## 傾斜角度の変更域の指定 zoom_range = 0.3, ## 拡大率の変更域の指定 channel_shift_range = 100, ## チャンネル変化の指定 ) |
チャンネル加工の影響に目が奪われますが、それ以外についてもよく見てみれば、拡大率が異なったり、表示位置が異なっていたりしていることが分かります。
まとめ
このように、keras の ImageDataGenerator を使用することで画像に対して加工処理を加えることができます。
今回は画像を9枚に複製したので、加工を行わなかった時に比べ、モデルの学習に当たって必要なデータを9倍量で用意できたことになります。
もちろん完全に異なる画像データと比べて取得できる特徴量は少ないため最善の方法では無いものの、画像データが少ないときに取るべき手段として優れたものであることが分かります。
以上。