Intelligent Technology's Technical Blog

株式会社インテリジェントテクノロジーの技術情報ブログです。

Android と TensorFlow: Google I/O 2017 の Codelab を試す

こんにちは。Google I/O 行きたかった中山です。
今年の I/O もいろいろと興味深い発表がありました。
多くの発表内容を効率的に理解する上で役立つのが、あわせて公開されている Codelabs です。
実際にコードを書きながら、動きを確認することができる、大変よいコンテンツだと思います。
今回は、Android からの取り扱いが劇的に簡単になったという、TensorFlow について、この Codelabs を試しながら、理解を深めてみたいと思います。

1.Codelab:「Android & TensorFlow: Artistic Style Transfer」について

今回試しますのは、Android アプリに TensorFlow を組み込んで、「Artistic style transfer」を試してみよう、というこちらの Codelab

「Artistic style transfer」というのは、ディープラーニングの仕組みを利用して、ある画像に、別の画像の表現スタイルだけを適用して、新しい画像を生成するもの。
具体的には、以下のように、

  1. 左の画像に、
  2. 真ん中の画像のスタイルを適用して、
  3. 右の画像を生成する、

というイメージです。
https://codelabs.developers.google.com/codelabs/tensorflow-style-transfer-android/img/c8b30d69a632f9a2.png

この Codelab で、「Artistic style transfer」の機能を試すことで、同時に、

  • TensorFlow のライブラリを、Android アプリで利用すること
  • 学習済みの TensorFlow のモデルを、Android アプリに組み込んで利用すること
  • 学習済みのモデルを元に「推論」のプロセスを実行すること
  • TensorFlow Graph の、特定の「ノード」に、アプリからアクセスできること

あたりを、合わせて確認できるようになっているようです。
細かく見ていくには、やはり TensorFlow 自体の理解、というのが必須になってはくるのですが、まず今回は、Android アプリから、こんなにシンプルに、TensorFlow の機能を利用できるのだ、というところに焦点をあてて、触っていきたいと思います。

なおこの Codelab を試すにあたって、利用したローカル側環境は以下の通りです。

  • Mac(macOS Sierra)
  • Android Studio(2.3.1)
  • Nexus5X

2.ソースコードの取得

ターミナルから、任意のディレクトリで以下コマンドを実行して、アプリのプロジェクトのソースコードを取得します。

git clone https://github.com/googlecodelabs/tensorflow-style-transfer-android

3.Android Studio で開く

git clone で取得してできた「tensorflow-style-transfer-android」フォルダ内にある「android」フォルダを選択して、Android Studio で開きます。
 
f:id:IntelligentTechnology:20170522171839p:plain:w440

ビルドが完了後、以下のようなファイル構成となっていることが、Android Studio 上で確認できます。
 
 f:id:IntelligentTechnology:20170522175308p:plain:w320

なおこの時点では、まだ TensorFlow 関連の処理は実装されていない、「枠」だけの状態です。
この状態でアプリを起動すると、以下のようになります。

https://codelabs.developers.google.com/codelabs/tensorflow-style-transfer-android/img/725a01c7c2722be7.png

上半分はカメラプレビュー、下半分は、「スタイル」を適用するためのボタンと、適用対象の「スタイル」の候補が並んでいます。

4.重要なメソッド

処理の中心となるのは、プロジェクト内に用意されている「StylizeActivity」クラスのようです。
このクラスの中の、以下のメソッドが、重要な役目をになっている、とのことです。

onPreviewSizeChosen

当アプリで実装している、カメラの機能が利用可能になった際に、このメソッドが呼び出されます。
初期設定のいろいろの処理をここで実行することになります。

setStyle

適用するスタイルを指定するメソッドです。

renderDebug

スタイル適用時のデバッグ情報を出力するメソッドです。

stylizeImage

指定されたスタイルを、画像に適用するメソッドです。

ImageUtils クラスの各種メソッド

プロジェクト内に用意されている ImageUtils クラスでは、画像処理に関するヘルパーメソッドがいくつか実装されています。
これらは、パフォーマンス対策のため、C++ で実装されたものを利用しています。コードの本体は、以下のように「libtensorflow_demo.so」としてあらかじめビルドされた状態で提供されています。
 
 f:id:IntelligentTechnology:20170523101043p:plain:w280

StylizeActivity クラスからも、この ImageUtils クラスのメソッドを利用しています。

5.TensorFlow 学習済みモデルについて

今回利用する学習済み TensorFlow モデル、以下の「stylize_quantized.pb」が本体となっています。
 
 f:id:IntelligentTechnology:20170523102534p:plain:w280

構成としては、こちらのページにも解説があるとおり、以下のように表現される、とのことなのですが、

https://codelabs.developers.google.com/codelabs/tensorflow-style-transfer-android/img/dac7f10dfec75e53.png

とりあえず、今回の Codelab を実行する限りにおいては、これらの詳しい仕様については、すべて把握できていなくても支障ないとのこと。
とはいえ、今後、TensorFlow をより使いこなしていくためには、このあたりの知識も必須になるのでしょう。

6.TensorFlow ライブラリの読み込み

build.gradle」ファイルを開いて、
 
 f:id:IntelligentTechnology:20170523110048p:plain:w320

ファイル末尾の「dependencies」ブロック内に、以下のように、「compile ・・・」を追記し、画面右上に表示される「Sync Now」をクリックします。

android {
  ・・・
  dependencies {
    compile 'org.tensorflow:tensorflow-android:1.2.0-preview'
  }
}

なんと、build.gradle ファイルに 1行書くだけで、TensorFlow ライブラリを利用できるようになりました。
以前は、TensorFlow 自体をビルドしておいて、NDK 入れて、・・・などなど下準備が大変だったようなのですが、時代は変わりました。

7.コードの追加

「StylizeActivity」クラスに、以下の private フィールドを追加します。

// TensorFlow ライブラリが提供する、
// TensorFlow の機能を呼び出すためのインタフェースとなるクラス
private TensorFlowInferenceInterface inferenceInterface;
// 学習済みモデルのファイル
private static final String MODEL_FILE = 
  "file:///android_asset/stylize_quantized.pb";
// TensorFlow の「ノード」の識別名
private static final String INPUT_NODE = "input";
private static final String STYLE_NODE = "style_num";
private static final String OUTPUT_NODE = 
  "transformer/expand/conv3/conv/Sigmoid";

また「onPreviewSizeChosen」メソッド内に、以下のように「inferenceInterface」フィールドを初期化するコードを追加します。

@Override
public void onPreviewSizeChosen(final Size size, final int rotation) {
  ・・・
  // inferenceInterface フィールドの初期化
  inferenceInterface = 
    new TensorFlowInferenceInterface(getAssets(), MODEL_FILE);
}

次に、「stylizeImage」メソッドの、

// TODO: Process the image in TensorFlow here.

と書かれている部分の下側に、以下のように、inferenceInterface のメソッドを呼び出すコードを追加します。

private void stylizeImage(final Bitmap bitmap) {
  ・・・
  // TODO: Process the image in TensorFlow here.
 
  // 元画像データを TensorFlow 側にコピー
  inferenceInterface.feed(INPUT_NODE, floatValues,
            1, bitmap.getWidth(), bitmap.getHeight(), 3);
  // 適用するスタイルの画像データを、TensorFlow 側にコピー
  inferenceInterface.feed(STYLE_NODE, styleVals, NUM_STYLES);
 
  // TensorFlow 側で、元画像データにスタイルを適用する処理を実行
  inferenceInterface.run(new String[] {OUTPUT_NODE}, isDebug());

  // 適用結果を取得して配列に保存
  inferenceInterface.fetch(OUTPUT_NODE, floatValues);
 
  ・・・

最後に、デバッグ用の出力を行う「renderDebug」メソッドにも一部コードを追加します。
こちらは必ずしも必要、というわけではないと思うのですが、ついでとして。
すでに実装されている、

final Vector<String> lines = new Vector<>();

というコードのすぐ下に、以下のようにコードを追加します。

private void renderDebug(final Canvas canvas) {
・・・
  final Vector<String> lines = new Vector<>();

  // TensorFlow の処理に関するデバッグ情報を出力
  final String[] statLines = inferenceInterface.getStatString().split("\n");
  Collections.addAll(lines, statLines);
  lines.add("");

  ・・・

これでコードの追加は完了です。
なお、このアプリ、サンプルのアプリの割には、けっこういろいろなことをやっているようで、「StylizeActivity」クラスにもともと実装されているコードは、意外とボリュームがあります。
TensorFlow の機能を呼び出す部分は、数行のコード追加で行えるようですが、それをアプリとしてきれいに実装するには、やはり相応の量のコードを記述する必要があるようです。

8.アプリを実行する

作成したアプリを Android 実機で実行します。
画面上側のカメラプレビューに映るイメージが、画面下側で選択したスタイルが適用された形で、リアルタイムに描画されます。

今回、その効果を検証しましたのは、私が毎週のようにお世話になっている、たも屋林店 の「ひやかけ1.5玉とちくわの天ぷら(わかめトッピング無料) 380円」。
ここ最近は気温も高くなり、ひやかけのおいしい季節になってまいりました。
 
 f:id:IntelligentTechnology:20170523133727j:plain:w400

これを、今回作成したアプリを通して見ると、以下のように表示されました。
 
 f:id:IntelligentTechnology:20170523133755p:plain:w400

まったくおいしくなさそうな絵になっていますが、スタイルの適用、という面では想定通りの結果となっているようです。

9.まとめ

TensorFlow に関しては、どちらかというと「学習させる」ほうがメインであるようにも思えますが、しかし、これまでは、学習済みのモデルをアプリから利用するだけでも、一手間かかっていた、という状況でした。
今回の Codelab のアプリ作成で、TensorFlow をマスターできる、というわけではまったくありませんけれども、それでも、build.gradle に 1行追加するだけで、容易に TensorFlow の機能を呼び出せる、というのを体験できる、という意味では、試しがいがあるではないか、と感じました。