ReactNative + Expo.io で素早く画像認識アプリを作る
最近、顔の美しさをアノテーションしたデータセットSCUT-FBP5500-Databaseを知り、これでイケメン度・美女度判定アプリをつくったらウケるのでは?と考えたりしています。
これに限らず、ポケモン図鑑的な(写真をとったら何か結果を返してくれる)アプリはいろいろと応用が効くため、前々から作ってみたいと思っていました。
そこで、今回比較的時間をかけずにAndroid, iOS共通のアプリが作れると噂のReactNative + Expo.ioを使った画像認識アプリの作成に挑戦しました。
作りたいアプリのイメージ
- アプリで写真を撮影する
- アプリで撮った写真を何らかの方法で画像認識する
- 結果をアプリに表示する
事前調査
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のモデルを動かせるか?
- Feature Requestは存在しますが、これは現状はできないようです。
- react-native-tensorflowというズバリみたいなパッケージもありますが、これは
react-native link
が必要なパッケージでExpoと両立させることはできません。
(※deatchすればできますが、detachするならそもそもExpoを使わないほうが良いと思います。)
ReactNative+Expo単体では画像認識が難しいので、base64でエンコードした画像をAPIにPOSTして結果を受け取るようにします。
GoogleのVision APIなどを使ってもよいですし、自力でAPIを作っても良いです。
最終的に作ったもの
Overview
以下のようなclient(アプリ)とserver(Webサーバ)を作ります。
server(Webサーバ)
- ソース
- flaskで実装、PCFでデプロイすることを想定しています。
/recognize
にbase64でエンコードした画像が入った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で実機にデプロイすることを想定しています。
- react-navigationを使って、画面遷移をする設定をします。
- 今回はOverviewに書いてある
MainScreen
(初期)とResultScreen
です。 stackNavigatiorを使うと自動的に戻るボタンが作成されるので便利です(※後のスクリーンショットを参照)
MainScreen
の実装が書いてあります。- 「Camera」や「CameraRoll」ボタンで画像を取得します。
- Expoに実装されているImagePickerを利用します。
- 使い方はこちらの記事がよくまとまっています。
- http://ykubot.com/2017/10/22/react-native-expo-image-picker/
- 取得した画像はImageManipulatorを使ってCropping, Resize, base64変換を行います(
_cropAndResize
関数) - 変換結果はstateに保存します。
- Expoに実装されているImagePickerを利用します。
- 「Recognize!」ボタンで
ResultScreen
に遷移します。このとき、stateのimageをResultScreen
に渡します。
ResultScreen
の実装が書いてあります。- 読み込み時に
MainScreen
から受け取った画像をserverにPOSTします(_getResultAsync
関数)- このとき、react-native-loading-containerを使って処理が終わるまでLoading画面が表示されます
- serverから結果が返ってきたら、結果をstateに保存してRenderします。
所管
ReactNative(Expo)は決められたプリセットを組み合わせるだけのアプリなら簡単に作れますが、 枠から外れた途端何もできなくなる(難しくなる)印象です。
以下のものは実装を検討しましたがExpoで簡単に実装するのは無理だと判断し、諦めました。
- KerasやTensorflowのモデルを動かす
- 撮影した画像の任意の場所でCroppingする
- Camera撮影画面をいじる
余談
SCUT-FBP5500-Databaseを使ったイケメン度・美女度判定アプリの大枠は無事完成しました。 が、このデータセットは研究目的でしか使えないことに気づいたので公開はしません。残念。