のどあめ

ググってもなかなか出てこなかった情報を垂れ流すブログ。

ReactNative + Expo.io で素早く画像認識アプリを作る

最近、顔の美しさをアノテーションしたデータセットSCUT-FBP5500-Databaseを知り、これでイケメン度・美女度判定アプリをつくったらウケるのでは?と考えたりしています。

これに限らず、ポケモン図鑑的な(写真をとったら何か結果を返してくれる)アプリはいろいろと応用が効くため、前々から作ってみたいと思っていました。

そこで、今回比較的時間をかけずにAndroid, iOS共通のアプリが作れると噂のReactNative + Expo.ioを使った画像認識アプリの作成に挑戦しました。

github.com

作りたいアプリのイメージ

  1. アプリで写真を撮影する
  2. アプリで撮った写真を何らかの方法で画像認識する
  3. 結果をアプリに表示する

事前調査

Expoの使い方

こちらの方の記事がよくまとまっています。
http://ykubot.com/2017/07/30/react-native-expo/

画面遷移の方法

今はreact-nativigationを使うのが主流だそうです。
こちらの記事がよくまとまっています。
https://bagelee.com/programming/react-native/react-navigation-react-native/

画像認識の方法

ReactNative+ExpoでkerasやTensorflowのモデルを動かせるか?

結論: APIを利用してfetchで取ってくる

ReactNative+Expo単体では画像認識が難しいので、base64エンコードした画像をAPIにPOSTして結果を受け取るようにします。
GoogleVision APIなどを使ってもよいですし、自力でAPIを作っても良いです。

最終的に作ったもの

github.com

Overview

以下のようなclient(アプリ)とserver(Webサーバ)を作ります。

f:id:ykicisk:20180531223550p:plain

server(Webサーバ)

  • ソース
  • flaskで実装、PCFでデプロイすることを想定しています。
  • /recognizebase64エンコードした画像が入ったjsonをPOSTすると、numpy.ndarrayに変換してshapeを返す仕様です。
    • 本当はここでkerasなりtensorflowなりでpredictした結果を返すことを想定しています。
@app.route('/recognize', methods=['POST'])
def recognize():
    if request.headers['Content-Type'] != 'application/json':
        print(request.headers['Content-Type'])
        return jsonify(res='error'), 400

    # decode base64 to image
    enc_data = request.json["base64"]
    dec_data = base64.b64decode(enc_data)
    img = Image.open(BytesIO(dec_data))
    x = np.asarray(img, dtype='uint8')

    # run something with 'x'

    return jsonify(result=str(x.shape))

client (アプリ)

  • ソース
  • 大抵はexpoで初期で作成されるファイルなので、.jsファイルだけ見ていただければと思います。
  • ReactNativeで実装、Expoで実機にデプロイすることを想定しています。

App.js

  • react-navigationを使って、画面遷移をする設定をします。
  • 今回はOverviewに書いてあるMainScreen(初期)とResultScreenです。 stackNavigatiorを使うと自動的に戻るボタンが作成されるので便利です(※後のスクリーンショットを参照)

src/componentes/Main.js

  • MainScreenの実装が書いてあります。
  • 「Camera」や「CameraRoll」ボタンで画像を取得します。
  • 「Recognize!」ボタンでResultScreenに遷移します。このとき、stateのimageをResultScreenに渡します。

f:id:ykicisk:20180531224419p:plain:w150
MainScreen
f:id:ykicisk:20180531224650p:plain:w150
ImagePicker利用時

src/componentes/Result.js

  • ResultScreenの実装が書いてあります。
  • 読み込み時にMainScreenから受け取った画像をserverにPOSTします(_getResultAsync関数)
  • serverから結果が返ってきたら、結果をstateに保存してRenderします。

f:id:ykicisk:20180531225322p:plain:w150
ResultScreen

所管

ReactNative(Expo)は決められたプリセットを組み合わせるだけのアプリなら簡単に作れますが、 枠から外れた途端何もできなくなる(難しくなる)印象です。

以下のものは実装を検討しましたがExpoで簡単に実装するのは無理だと判断し、諦めました。

  • KerasやTensorflowのモデルを動かす
  • 撮影した画像の任意の場所でCroppingする
  • Camera撮影画面をいじる

余談

SCUT-FBP5500-Databaseを使ったイケメン度・美女度判定アプリの大枠は無事完成しました。 が、このデータセットは研究目的でしか使えないことに気づいたので公開はしません。残念。