【プラモデル】AIの力で主砲発射！

今回は個人の趣味で書いています。
製造業では**一切役立たない技術**ですので、ご注意ください。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/67ef0a65-cb30-9611-8c3b-0b7733cdd81b.png)

※本稿は[【Zoom】Pythonデータ分析勉強会#13　その1](https://pythondata.connpass.com/event/146737/)の発表資料です。

#男の子の憧れ
プラモデルって楽しいですよね。私も小学校の頃はよく作っていました。

そして、プラモデルを作る男の子はよく憧れます、それは...
「プラモデルの**主砲**からドンッ！と発射させたい」

ガンプラでいえば、ビームライフルやメガ粒子砲。
戦車でいえば、主砲。これらを発射させたいと思うわけです。

もちろん、BB弾などではなく、**ビームや火球**が出るようなエフェクトを
再現したいと熱烈に思います。

そこで、AIの力を借りてこれを実現してみました。

#何をしたのか？

CycleGANを使って、プラモデルの写真から主砲を発射させました。

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/a4b41aba-fba3-d335-b606-0963879c6486.png)

（元画像は[こちら](https://hobbycom.jp/my/b500e6af02/photo/products/36643)）

プラモデルの写真を撮り、CycleGANで処理することにより、
主砲が発射されたような画像が生成されます。

#CycleGAN
今回使ったCycleGANは、2年前に発表された少し古い手法ですが、集める画像の
構図に制約がないために非常に使いやすい手法です。

CycleGANの威力は、以下の動画を見れば一目瞭然だと思います。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">店舗向けにこういうAR演出装置作るのありだな。iOS13入れたiPhoneと箱とディスプレイあれば作れるから、かなり安価で作れるはず。<br><br> <a href="https://t.co/b1GvDnhWOc">pic.twitter.com/b1GvDnhWOc</a></p>&mdash; KAJI / MESON CEO 😎 (@kajikent) <a href="https://twitter.com/kajikent/status/1159282037162799104?ref_src=twsrc%5Etfw">August 8, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

（twitterより転載）

この動画では、使った手法は明記されておりませんが、CycleGANもしくは
その進化系の手法だと思われます。

CycleGANは、端的にいうと、パターンAとパターンBの画像を適当に集めて
学習させれば、A－B間を変換するジェネレーターが勝手に構築されます。

上記の動画でいうと、パターンAは「人の腕」、パターンBは「サイボーグの腕」
の画像を集めて学習されば「人の腕」→「サイボーグの腕」への変換ジェネレーターが
構築されます。詳しくは以下の記事を参考にしてください。
https://qiita.com/itok_msi/items/b6b615bc28b1a720afd7

##データ
今回のチャレンジでいうと、パターンAは「プラモデルの写真」、パターンBは
「実弾を打つ戦車の写真」です。これらをCycleGANで学習させれば

「プラモデルの写真」→「**実弾を打つ**プラモデルの写真」

への変換が可能になります。

[こちら](https://ledge.ai/cyclegan/)の記事によると、写真の構図は「拡大や縮小が混じったもの」や
「関係ないもの」が映ったものが良いらしいので、それらを意識して
以下の枚数を集めました。

||枚数|
|---|---|
|プラモデルの写真|153|
|実弾を打つ戦車の写真|114|

#CycleGAN on TensorFlow
CycleGANは、著者から[コード](https://github.com/junyanz/CycleGAN)が公開されていますが、Colabで動かすことを
念頭に置き、今回はTensorFlowのチュートリアルのコードをいじって
学習させました。
https://www.tensorflow.org/beta/tutorials/generative/cyclegan

いじる部分は

```python
def preprocess_image_train(image, label):
  image = random_jitter(image)
  image = normalize(image)
  return image

def preprocess_image_test(image, label):
  image = normalize(image)
  return image

train_horses = train_horses.map(
    preprocess_image_train, num_parallel_calls=AUTOTUNE).cache().shuffle(
    BUFFER_SIZE).batch(1)

train_zebras = train_zebras.map(
    preprocess_image_train, num_parallel_calls=AUTOTUNE).cache().shuffle(
    BUFFER_SIZE).batch(1)

test_horses = test_horses.map(
    preprocess_image_test, num_parallel_calls=AUTOTUNE).cache().shuffle(
    BUFFER_SIZE).batch(1)

test_zebras = test_zebras.map(
    preprocess_image_test, num_parallel_calls=AUTOTUNE).cache().shuffle(
    BUFFER_SIZE).batch(1)
```

の部分を以下のように変更しました。

```python
from google.colab import drive
drive.mount('/content/drive')
%cd /content/drive//My Drive/CycleGAN

import glob
import numpy as np

# プラモ
koma_dir = "puramo/*.jpg"
koma_labels = np.ones(153)
koma_filenames = glob.glob(koma_dir) # filenameのリスト取得 (CSVとかから読んでも良い)
train_horses = tf.data.Dataset.from_tensor_slices((koma_filenames, koma_labels))

# 実弾発射
gold_dir = "heat/*.jpg"
gold_labels = np.zeros(114)
gold_filenames = glob.glob(gold_dir) # filenameのリスト取得 (CSVとかから読んでも良い)
train_zebras = tf.data.Dataset.from_tensor_slices((gold_filenames, gold_labels))

# test
test_dir = "test/*.jpg"
test_labels = np.zeros(11)
test_filenames = glob.glob(test_dir) # filenameのリスト取得 (CSVとかから読んでも良い)
test_horses = tf.data.Dataset.from_tensor_slices((test_filenames, test_labels))

test_zebras = train_zebras

def preprocess_image_train(image, label):
  image = tf.image.decode_jpeg(tf.io.read_file(image))
  image = random_jitter(image)
  image = normalize(image)
  return image

def preprocess_image_test(image, label):
  image = tf.image.decode_jpeg(tf.io.read_file(image))
  image = test_jitter(image)
  image = normalize(image)
  return image
```

Googleドライブにマウントした後に、プラモデルの写真153枚と実弾を打つ
写真114枚、テスト画像11枚を読み込んでいます。

TensorFlowのデータ読み込みは以下の記事を参考にしました。
https://qiita.com/S-aiueo32/items/c7e86ef6c339dfb013ba

#結果
著作権の関係で、元画像を掲載するのは避けますが、基本的に背景は
無地なものです。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/67ef0a65-cb30-9611-8c3b-0b7733cdd81b.png)
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/a4b41aba-fba3-d335-b606-0963879c6486.png)
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/728c6e9c-893d-0a06-ed56-8c5e2ff58968.png)
![03_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/dd4ef2d0-cf25-cc0f-f099-154b89a45583.png)
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/d1a1f223-9519-a743-2bb6-17bae37da39b.png)
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/365e55c4-b388-b49d-d263-3b5dc7d52187.png)

ちなみにEpochは70。Colabで約2時間あれば学習完了です。

冒頭でご紹介したPythonデータ分析勉強会では、元画像をお見する
予定です。

#おまけ
ちなみに、TensorFlowのチュートリアルをColabで**200エポック（46時間）**回すと
以下のような画像が生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/4febd600-dbe4-8f9b-7f74-80edde499488.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/5d2ea1be-0724-e7e3-ddcb-1a3eb50a9f4b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/255b8462-0ee6-62a2-7a8c-cdb083f4ea1e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/9bfa6817-2199-44e0-0fa5-f20a16ce3f3a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264781/3a3af90c-16b6-0b42-995c-c4e06bce3c17.png)


凄いですね！
Colabに余裕がある人はやってみては、いかがでしょうか。

このチュートリアル、Googleドライブにマウントすることで、ちゃんとcheckpointと
モデルを保存してくれます。そのため、Colabの12時間ルールを超えて学習させることが
可能です。

#まとめ
+ CycleGANはデータ集めが非常に**楽**。
+ TensorFlowのチュートリアルを使えば、誰でも気軽に使える。
+ スポーツの中継とかでも使える技術だと思う。