TensorFlowの学習モデルによるモバイル端末でのリアルタイム画像認識

タイトルは論文っぽく書いていますが、要はTensorFlowのページにAndroid / iOS で動かせるぜーとあったのでどんなものかやってみた、という話です。
やってみると、確かにAndroid、iOS両方でビルドすることができたので、その手順などを書きたいと思います。

# Android

まずはAndroid版から。
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/android
基本的にはこちらに従います。ビルドできると、GoogleのInceptionモデルを使ったTF Classify,  Scalable Object Detection using Deep Neural Networks をベースにしたTF Detectが作られます。
さらににスタイル変換するTF Stylizeまで実装されていたらしい…ので改めて以下の環境でビルドし直してみました。 
- Android Studio 2.2.3
- Android 6.0.1 (Xperia Z3)

## TensorFlowのビルド

まずは最新版をクローンしてきます。いまTensorFlowはr0.12だったような。

```
git clone --recurse-submodules https://github.com/tensorflow/tensorflow.git
```
次にビルドツールbazelをインストールします。

```
brew install bazel
```
C/C++コードをコンパイルするためにNDKが必要です。
https://developer.android.com/ndk/guides/index.html
によると、Preferencesからsdkで検索して `SDK Tools` タブを開きます。ここで

- CMake
- LLDB
- NDK

にチェックを入れてApplyすればインストールできます。SDKについてはとなりの `SDK Platforms` からインストールできて、Android 5.0以上はすべてインストールしておきました。
<img width="80%" alt="new_screen_shot.png" src="https://qiita-image-store.s3.amazonaws.com/0/137365/a6e70053-b00b-dbc8-def8-f503b6d2f6ef.png">



続いて、 `/tensorflow/WORKSPACE` ファイルを編集します。コメントアウトされている部分を自分の設定に直します。

```
 Uncomment and update the paths in these entries to build the Android demo.
-#android_sdk_repository(
-#    name = "androidsdk",
-#    api_level = 23,
-#    build_tools_version = "23.0.1",
-#    # Replace with path to Android SDK on your system
-#    path = "<PATH_TO_SDK>",
-#)
-#
-#android_ndk_repository(
-#    name="androidndk",
-#    path="<PATH_TO_NDK>",
-#    api_level=21)

+android_sdk_repository(
+    name = "androidsdk",
+    api_level = 25,
+    build_tools_version = "25.0.2",
+    # Replace with path to Android SDK on your system
+    path = "/Users/user-name/Library/Android/sdk/",
+)
+
+android_ndk_repository(
+    name="androidndk",
+    path="/Users/user-name/Library/Android/sdk/ndk-bundle/",
+    api_level=21)
```
以下でTensorFlowの学習モデルを落として適切な場所に配置します。モデルはassetsディレクトリに置けばよいことがわかります。学習モデルであるグラフ: `tensorflow_inception_graph.pb` は53.9MBでした。またdetectionのモデルグラフ: `multibox_model.pb` は18.6MBのようです。

```
$ curl -L https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip -o /tmp/inception5h.zip
$ curl -L https://storage.googleapis.com/download.tensorflow.org/models/mobile_multibox_v1.zip -o /tmp/mobile_multibox_v1.zip

$ unzip /tmp/inception5h.zip -d tensorflow/examples/android/assets/
$ unzip /tmp/mobile_multibox_v1.zip -d tensorflow/examples/android/assets/
```

これでビルドする準備が整ったのでルートディレクトリ(WORKSPACEが置いてあるところ)でbazel buildします。

```
$ bazel build -c opt //tensorflow/examples/android:tensorflow_demo
```

20分くらいかかるので、エスプレッソでも抽出してましょう。無事通ったら、実際に端末にインストールして動かしてみます。

```
$ adb install -r bazel-bin/tensorflow/examples/android/tensorflow_demo.apk
```

以下のようなエラーがさっき出たらupdateしろとのことなので、従うとちゃんとビルドできました。

```
$ adb install -r bazel-bin/tensorflow/examples/android/tensorflow_demo.apk
It appears you do not have 'Android SDK Platform-tools' installed.
Use the 'android' tool to install them:
    android update sdk --no-ui --filter 'platform-tools'
```

<img height="600px" alt="new_screen_shot.png" src="https://qiita-image-store.s3.amazonaws.com/0/137365/204325ff-7eee-d1f3-170a-e6efae8ce68a.png">


先週は割にぬるぬる動いた印象だったのにビルドし直したらちょいと重くなったような印象が…気のせいかもしれませんが。Scalable Object Detectionのアプリケーションもインストールされているはずです。そしてスタイル変換はとてもよくできていて、画面下に絵画のサンプルがあって、それをどのくらいスタイルとして用いるかを選べるUIになっていました。解像度が32〜1024まで選べるようになっていて、さすがに192くらいからはリアルタイムではまわりませんが、画像生成できていました。

<img height="600px" alt="new_screen_shot.png" src="https://qiita-image-store.s3.amazonaws.com/0/137365/e34d3645-3602-2a47-951d-d72d856131a9.png">

ソースコードのコミットを見ると、わずか1週間の間で `StylizeActivity.java` と `TensorFlowYoloDetector.java` が増えていた…びっくり。

# iOS

続いてiOSです。基本的には
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/ios_examples
にある `TensorFlow iOS Examples` に従います。Xcode 7.3以降が必要とあって、iOSをあげてしまっていたのもあってXcodeをアップデートして、以下の環境でビルドできました。

- Xcode 8.2.1
- iOS 10.1.1 (iPhone SE)

## TensorFlowのビルド

まずは
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/makefile
のiOSの項に従ってTensorFlowをビルドせよとあるのでやります。

```
xcode-select --install
brew install automake
brew install libtool
```
をインストール。次のシェルで1発でビルドできるようです。

```
tensorflow/contrib/makefile/build_all_ios.sh
```
これによって `tensorflow/contrib/makefile/gen/lib/libtensorflow-core.a` が生成されるようです。これをXcode Projectでリンクしてあげればよいらしい。20分近くかかるのでコーヒーでも淹れましょう。このシェルではなく1つ1つ生成しようとすると、まずはdependenciesをダウンロード。

```
tensorflow/contrib/makefile/download_dependencies.sh
```
次に、iOS向けにprotobufをコンパイル。

```
tensorflow/contrib/makefile/compile_ios_protobuf.sh 
```
そしてiOSをターゲットにmake。

```
make -f tensorflow/contrib/makefile/Makefile \
 TARGET=IOS \
 IOS_ARCH=ARM64
```

## Inceptionモデルのダウンロードとプロジェクトの実行

TensorFlowディレクトリのルートで、以下を実行するか、<a href="https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip">リンク</a>からInception v1のモデルをダウンロードして解凍します。

```
mkdir -p ~/graphs
curl -o ~/graphs/inception5h.zip \
 https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip \
 && unzip ~/graphs/inception5h.zip -d ~/graphs/inception5h
cp ~/graphs/inception5h/* tensorflow/contrib/ios_examples/benchmark/data/
cp ~/graphs/inception5h/* tensorflow/contrib/ios_examples/camera/data/
cp ~/graphs/inception5h/* tensorflow/contrib/ios_examples/simple/data/
```
これを見ると、アプリのサンプルは `benchmark` ,  `camera` , `simple` の3つあることがわかります。画像認識をするのはもちろん `camera` です。inceptionのモデルはそれぞれのアプリのdataディレクトリ配下に置けばよさそうです。これが終わったら、あとはディレクトリの中にあるcamera_example.xcodeprojファイルを開いてrunするだけです。
<img width="320" alt="IMG_0155.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/137365/8b584239-ffbf-248f-9bce-42a833628c0d.png">



Android版とはちがって、画面下に画像取得を止められる機能がついていました。手持ちのiPhone SEでぬるぬる動きます。
http://qiita.com/shu223/items/ce190ea6669b2636a8a7
の記事によると、iPhone6で数秒の遅延があるということだったので、モデル自体がそのときと比べて小さくなったのか、記事で言及されているGPU Acceleratedをサポートしたのか、iOS 10 で Accelerate.framework に追加された BNNSが実装されたのか（issueがcloseしてないのでまだっぽい…）
逆に実装されてなくてこの速さなら、iPhone 6s以降であればもっとfpsをあげられなくもなさそう。

# まとめ

TensorFlowの学習モデルをモバイルで動かすサンプルをビルドしてみました。Android/iOSどちらでも結構ぬるぬる動いていて、どれくらい重たいモデルまでいけるのかが気になるところ。