TensorFlow-Object-Detection(物体認識)を使って学習モデルの作成からiOSでビルドするまでの道のり【iOSでビルド】

## 前回まで
前回はTensorFlowを使って学習させるまでを行いました。今回は学習したモデルを実際にiOSに組み込んでスタンプを認識させるまでを記録します。
※私は機械学習については全くの素人です。

##### おおまかな流れ
1. 学習準備
 1. TensorFlowのセットアップ 
 * 対象画像を集める
 * 画像ごとにラベリング
 * TFRecordを作成
2. 学習
 1. モデル学習用のconfigファイルを用意
 * 学習
 * 学習過程を確認
 * 学習モデルの書き出し
3. **iOSに組み込む**
 1. **ビルドの下準備**
 * **インストール**

##### iOSbuildの主なフォルダ構成
```
|-tensorflow(checkout/r1.12)
|-tensorflowiOS
    |-Models
        |-label_map.pbtxt
        |-trainval.txt
        |-Models
            |-ssd_model
              |-frozen_inference_graph.pb
              |-label_map.txt
    |-swift
```

## ビルドの下準備
すでに賢い方がサンプルを作っていますので、それを使わせてもらいます。
[こちらのリンク](https://github.com/csharpseattle/tensorflowiOS)からソースコードを落としてきて、READMEに従ってセットアップします。以下、READMEの焼き増しの内容です。


```bash:ライブラリをインストール
brew install automake libtool
```
```bash:tensorflowをclone
git clone https://github.com/tensorflow/tensorflow

cd tensorflow
```

落としてきたtensorflowフォルダで今後の過程で使用する`ANDROID_TYPES`がサポートされているバージョンをチェックアウトします。上記リポジトリのオーナーは`v1.12.0`まで確認したそうです。念の為`git tag`でバージョンを確認し、v1.12.0をチェックアウトします

```bash
git checkout -b v1.12.0 refs/tags/v1.12.0

or

git checkout origin/r1.12
```


以下のエラーに遭遇したら
> thread-local storage is not supported for the current target

こちらのコマンドを実行

```bash
brew install gnu-sed

gsed '/ifeq[^,]*,I386)/!b;n;n;n;n;n;s/thread_local//' < ./tensorflow/contrib/makefile/Makefile > foo; mv foo ./tensorflow/contrib/makefile/Makefile
gsed 's/thread_local int per_thread_max_parallism/__thread int per_thread_max_parallism/' < tensorflow/core/util/work_sharder.cc > foo; mv foo ./tensorflow/core/util/work_sharder.cc
```

tensorflowをビルド

```bash:/tensorflow
export ANDROID_TYPES="-D__ANDROID_TYPES_FULL__"
```
階層を一つ上げて(tensorflowがあるディレクトリ）iOSでtensorflowをビルドするために必要なライブラリをmakeします。ビルドには2時間ほどかかります。

```bash:/
tensorflow/contrib/makefile/build_all_ios.sh
```

## インストール
ビルドファイルが作成できたら、実際に学習モデルを使ってアプリをインストールしてみます。
サンプルコードがあるリポジトリ`tensorflowiOS`には`objC`と`swift`と２つの言語のバージョンがありますが、私はswiftを選択しました。

まず、このフォルダにある`tensorflow.xconfig`で先ほどのtensorflowリポジトリのパスに変更します。

```bash:/tensorflowiOS/swift/SupportingFiles/tensorflow.xcconfig
TENSORFLOW_ROOT = /path/to/tensorflow
```

次に、学習モデルをプロジェクトに追加します。
前回学習して書き出したフォルダにある`frozen_inference_graph.pb`と`tensorflow-stamp-model/annotations/label_map.pbtxt`を/tensorflowiOS/Models/にコピーします。また、この際、`label_map.pbtxt`を**`label_map.txt`**にrenameします。

```bash:/tensorflowiOS/Models/に入れるデータ
frozen_inference_graph.pb
label_map.txt
```

認識した物体のラベル名を取得するために、label_map.txtのパラメータに`display_name`を追加します。追加しなくても動作しますが、認識した物体が何かわかりません。。。あとで気づきました。

```json:label_map.txt
item {
  id: 1
  name: 'tent'
  display_name: 'tent'
}
item {
  id: 2
  name: 'build'
  display_name: 'build'
}
item {
  id: 3
  name: 'house'
  display_name: 'house'
}
```


コピーしたファイルはxcodeのプロジェクトにもドラッグ&ドロップして追加します。
そして、`TensorflowGraph.mm`で使用するモデルのファイル名を編集します。

```swift:tensorflowiOS/swift/Tensorflow/TensorflowGraph.mm
NSString *model = @"frozen_inference_graph";
NSString *label = @"label_map";
```

以上でiOSでビルドするための準備が整いました。Runするとインストールされると思いますが、私は以下のエラーに遭遇したので、その修正方法を記録しておきます。

> xcrun: error: SDK "iphoneos" cannot be located

```
 # このエラーが出たら以下を実行
 sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/
 ```

> failed to load model

```
# パスがうまく通っていない可能性がある
export ANDROID_TYPES="-D__ANDROID_TYPES_FULL__"
```

<img width="375" alt="Image from iOS (1).png" src="https://qiita-image-store.s3.amazonaws.com/0/117549/72faa28c-3ddb-794e-6be8-f24e80f91675.png">


## 参考
[Tensorflow Object Detection APIで寿司検出モデルを学習するまで](https://qiita.com/watanabe0621/items/0b1cfa2d89c8321767e2)
[Tensorflow ObjectDetection APIでミレミアム・ファルコン検出](https://qiita.com/YudaiSadakuni/items/530ee4150cb3ec020010#_reference-fdda7169c9b19ee7028b)

---
動作保証はしませんが、今回のTensorFlowのプロジェクトは[GitHub](https://github.com/mitsuyacider/tensorflow-stamp-model)に置いてあります。
また、TensorFlowの学習モデルをiOSに組み込んだプロジェクトは[こちら](https://github.com/mitsuyacider/tensorflow-ios-build)です。ご自由にお使いください。