ML Kit for Firebaseを利用してAndroidで文字認識
今回はGoogleのサービスFirebaseの機能の一つであるML Kitで文字認識を試してみたお話です。
FirebaseはMBaaSと呼ばれるサービスで、Mobile用のバックエンド機能をクラウドで提供するものです。Android、iOSから利用できます。ML KitはGoogle I/O2018で発表された、Firebase上で機械学習のモデルを利用して画像認識などの機能を利用することができます。わたしは大学時代に機械学習を研究で少し利用していました(DNNが流行る直前くらいの時期ですが)。社会人になってからもニューラルネットワークフレームワークのChainerを少し触ったこともあり、ML Kitも興味があったので試してみました。
ML Kit for Firebaseの文字認識
Firebaseの準備
まずはML Kit fro Firebaseを利用する準備を行います。
登録できたらFirebase consoleで新規プロジェクトを作成します。
プロジェクト名には分かりやすい名前を付けておきましょう。
プロジェクトを作成したらAndroidアプリにFirebaseを追加します。
まず、Androidパッケージ名を決めます。Android Studioのプロジェクト作成時に設定するパッケージ名にも同じパッケージ名を利用します。次に、Android Studioのプロジェクトに読み込ませるjsonファイルをダウンロードして配置。最後にFirebase SDKを利用する設定をbuild.gradleに記述して準備完了です。Android Studioのプロジェクトを同時進行で準備しながら作業をすればよいと思います。
案内モーダルがとても分かりやすいのですが、1つ注意点があります。アプリレベルbuild.gradleの記述で'compile'となっている部分は、現在は'implementation'に変更されています。'compile'と記述するとgradleの同期で失敗するので気を付けましょう。
これでML Kitを利用する準備は完了です。
Androidで文字認識を動かす
ML Kitのそれぞれの機能にも公式のチュートリアルがあり、そちらがとても分かりやすく説明してくれています。文字認識についてもこちらを参考に行えば、Android開発の経験がある方ならそれほど迷わずに文字認識を実行できると思います。
Kotlinのチュートリアルもあったのですが、わたしはJavaでしか開発を行ったことがなかったので今回はJavaでコードを記述しました。
ML Kitはクラウドベースでサーバーに処理をお願いする方法とOn Deviceで行う方法があるようです。それぞれの特徴は
On Device
- 無料
- 高速
- ラテン文字のみ認識可能
- 1000アクセス/月までは無料
- 高精度
- 多言語文字対応
今回はOn Deviceのみ試してみました。雑ですが、コードは以下のようになります。
package jp.koda.mlkit.ocrtest; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.Task; import com.google.firebase.ml.vision.FirebaseVision; import com.google.firebase.ml.vision.common.FirebaseVisionImage; import com.google.firebase.ml.vision.text.FirebaseVisionText; import com.google.firebase.ml.vision.text.FirebaseVisionTextDetector; public class MainActivity extends AppCompatActivity { private final static int RESULT_CAMERA = 1; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = findViewById(R.id.imageView); Button takePictureButton = findViewById(R.id.take_picture_button); takePictureButton.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(intent, RESULT_CAMERA); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if(requestCode == RESULT_CAMERA) { Bitmap bitmap; if(intent == null) { return; } else { bitmap = (Bitmap) intent.getExtras().get("data"); } imageView.setImageBitmap(bitmap); FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap); FirebaseVisionTextDetector detector = FirebaseVision.getInstance().getVisionTextDetector(); Task<FirebaseVisionText> result = detector.detectInImage(image) .addOnSuccessListener(new OnSuccessListener<FirebaseVisionText>() { @Override public void onSuccess(FirebaseVisionText firebaseVisionText) { for (FirebaseVisionText.Block block: firebaseVisionText.getBlocks()) { Rect boundingBox = block.getBoundingBox(); Point[] cornerPoints = block.getCornerPoints(); String text = block.getText(); Log.i("OCR_RESULT", text); for (FirebaseVisionText.Line line: block.getLines()) { // ... for (FirebaseVisionText.Element element: line.getElements()) { } } } } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } }); } } }
実機で文字認識させてみましたが、かなり精度よく出来ている印象でした。本当はBitmapを利用する方法ではなく、画像の回転情報もあつかえるByteBufferなどを利用する方法のほうがいいのかもしれません。