よっしーの私的空間

機械学習を中心に興味のあることについて更新します

ImageDataGeneratorでデータ拡張してみた

ImageDataGeneratorを使って画像拡張を色々試してみます。使う画像は実家のワンコ(小次郎君)の写真です。
f:id:t-yoshi-book:20210917125733p:plain

1.画像の水増し

① 回転(rotation_range)
datagen = ImageDataGenerator(
    rotation_range=90,
)
g = datagen.flow(x, batch_size=x.shape[0], shuffle=False)

itr=3
generated_imgs = []
for i in range(itr):
    generated_img = g.next()
    generated_imgs.extend(generated_img)
  • 入力画像はx。x.shape=(1, 1478, 1108, 3)=(画像1枚、縦幅1478ピクセル、横幅1108ピクセル、カラー画像)。
  • 出力画像はgenerated_imgs。generated_imgs.shape=(3, 1478, 1108, 3)=(画像3枚、縦幅1478ピクセル、横幅1108ピクセル、カラー画像)。

拡張された画像を確認します。

generated_imgs = np.array(generated_imgs)
fig = plt.figure(figsize=(15,5))
fig.add_subplot(1,itr+1,1)
plt.title("original img")
plt.imshow(x[0])

for i,img in enumerate(generated_imgs):
    fig.add_subplot(1,itr+1,i+2)
    plt.title("aug img {}".format(i))
    plt.imshow(img)

plt.show()

f:id:t-yoshi-book:20210917131054p:plain

② 横にずらす(width_shift_range)
datagen = ImageDataGenerator(
    width_shift_range=0.5,
)

f:id:t-yoshi-book:20210917131531p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

③ 縦にずらす(height_shift_range)
datagen = ImageDataGenerator(
    height_shift_range=0.5,
)

f:id:t-yoshi-book:20210917140903p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

④ 明るさを変える(brightness_range)
datagen = ImageDataGenerator(
    brightness_range=[0.5,1.5],
)

f:id:t-yoshi-book:20210917141134p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

⑤ 斜めにひっぱる(shear_range)
datagen = ImageDataGenerator(
    shear_range=50,
)

f:id:t-yoshi-book:20210917141428p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

⑥ 拡大縮小(zoom_range)
datagen = ImageDataGenerator(
    zoom_range=0.5,
)

f:id:t-yoshi-book:20210917141657p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

⑦ RGB各チャネルに0~指定した値をランダムに加算または減算(channel_shift_range)
datagen = ImageDataGenerator(
    channel_shift_range=100,
)

f:id:t-yoshi-book:20210917142444p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

⑧ 水平方向に反転(horizontal_flip)
datagen = ImageDataGenerator(
    horizontal_flip=True,
)

f:id:t-yoshi-book:20210917143114p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

⑨ 垂直方向に反転(vertical_flip)
datagen = ImageDataGenerator(
    vertical_flip=True,
)

f:id:t-yoshi-book:20210917144750p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

参考:余白の埋め方(fill_mode, cval)

画像のずらしたりするときに、画像に余白ができることがあります。その処理の仕方が複数用意されているので、それぞれ試してみます。cvalの値によって、塗りつぶしの色が変わります。色は白(0)~黒(255)です。

① constant, cval

余白を単色で塗りつぶす。

datagen = ImageDataGenerator(
    width_shift_range=0.5,
    fill_mode='constant', 
    cval=0.0, #ココの値で色を変える
)

f:id:t-yoshi-book:20210917162446p:plain
f:id:t-yoshi-book:20210917162638p:plain
f:id:t-yoshi-book:20210917162730p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

② nearest

隣接しているピクセルの色をコピーして余白を埋めます。ちなみにfill_modeのデフォルトはnearestです。

datagen = ImageDataGenerator(
    width_shift_range=0.5,
    fill_mode='nearest', 
)

f:id:t-yoshi-book:20210917163239p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

③ reflect

画像を反転させて余白を埋めます。

datagen = ImageDataGenerator(
    width_shift_range=0.5,
    fill_mode='reflect', 
)

f:id:t-yoshi-book:20210917163936p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

④ wrap

画像を複製して余白を埋めます。

datagen = ImageDataGenerator(
    width_shift_range=0.5,
    fill_mode='wrap', 
)

f:id:t-yoshi-book:20210917170616p:plain
※コードは 「① 回転」とほぼ同じなので重複分は省略。

2.正規化

2.1.画像単位に正規化

① 平均を0にする(samplewise_center)
datagen = ImageDataGenerator(
    samplewise_center=True, 
)

画像のデータの平均が0になるように正規化します。以下を見てもらえれば分かると思いますが、全データに対して平均値を減算しています。
f:id:t-yoshi-book:20210917174639p:plain

標準偏差で正規化(samplewise_std_normalization)
datagen = ImageDataGenerator(
    samplewise_std_normalization=True, 
)

標準偏差で正規化します。イメージは以下の通りです。
f:id:t-yoshi-book:20210917182254p:plain

2.2.データセット単位に正規化

① 平均を0にする(featurewise_center)

実装する上での注意点ですが、データセット単位に正規化する場合はImageDataGenerator.fit()を呼び出す必要があります。fit()でデータセット全体の統計量を計算しているようです。

datagen = ImageDataGenerator(
    featurewise_center=True, 
)
datagen.fit(x)
g = datagen.flow(x, batch_size=x.shape[0], shuffle=False)

画像のデータをデータセット全体の平均値で正規化します。イメージは以下の通りです。samplewizseのときはチャネル関係なく全データの平均を使って平均していましたが、featurewiseの方はチャネル毎に平均を取って正規化しているようです。何故そのような仕様になっているのかは分からないです。分かる方いらっしゃったらコメントいただけると嬉しいです。
f:id:t-yoshi-book:20210917185006p:plain
※上記の数式はあくまでイメージです。雑な表現ではありますが、ご容赦ください。
※微妙に計算合わないですが、端数処理ですかね…?

標準偏差で正規化(featurewise_std_normalization)
datagen = ImageDataGenerator(
    featurewise_std_normalization=True, 
)
datagen.fit(x)
g = datagen.flow(x, batch_size=x.shape[0], shuffle=False)

こちらもチャネル毎に正規化しているようです。イメージは以下の通り。
f:id:t-yoshi-book:20210917190546p:plain
※こちらも雑な表現ご容赦ください。
※同じく、微妙に計算が合わない。

③ ZCA白色化(zca_whitening)

ZCA白色化は大量にメモリを食うようです。1280×960の画像でZCA白色化を実施しようとしたところ、メモリ49.4TiB必要だって怒られました。しょうがないので、めちゃめちゃ画像を縮小させました。

datagen = ImageDataGenerator(
    zca_whitening=True,
    zca_epsilon=1e-06,
)
datagen.fit(x)
g = datagen.flow(x, batch_size=x.shape[0], shuffle=False)

共分散行列が単位行列になるように変換しているようです。ZCAイプシロンがどう使われていて、どのような影響を及ぼすのかは正直分からないです。ZCAイプシロンをデフォルトの1e-06としたときに以下のように画像データが変換されていました。
f:id:t-yoshi-book:20210917193603p:plain