ITと雑記とド田舎と

ド田舎在住エンジニアがIT備忘録と雑記を書くブログです

ML Kit for Firebaseを利用してAndroidで文字認識

今回はGoogleのサービスFirebaseの機能の一つであるML Kitで文字認識を試してみたお話です。

FirebaseはMBaaSと呼ばれるサービスで、Mobile用のバックエンド機能をクラウドで提供するものです。AndroidiOSから利用できます。ML KitはGoogle I/O2018で発表された、Firebase上で機械学習のモデルを利用して画像認識などの機能を利用することができます。わたしは大学時代に機械学習を研究で少し利用していました(DNNが流行る直前くらいの時期ですが)。社会人になってからもニューラルネットワークフレームワークChainerを少し触ったこともあり、ML Kitも興味があったので試してみました。

 

ML Kit  for Firebaseの文字認識

Firebaseの準備

まずはML Kit fro Firebaseを利用する準備を行います。

f:id:kdn-1017-wttd:20180710064448p:plain

 

登録できたらFirebase consoleで新規プロジェクトを作成します。

f:id:kdn-1017-wttd:20180710065143p:plain

プロジェクト名には分かりやすい名前を付けておきましょう。

f:id:kdn-1017-wttd:20180710065332p:plain

プロジェクトを作成したらAndroidアプリにFirebaseを追加します。

f:id:kdn-1017-wttd:20180710065656p:plain

まず、Androidパッケージ名を決めます。Android Studioのプロジェクト作成時に設定するパッケージ名にも同じパッケージ名を利用します。次に、Android Studioのプロジェクトに読み込ませるjsonファイルをダウンロードして配置。最後にFirebase SDKを利用する設定をbuild.gradleに記述して準備完了です。Android Studioのプロジェクトを同時進行で準備しながら作業をすればよいと思います。

案内モーダルがとても分かりやすいのですが、1つ注意点があります。アプリレベルbuild.gradleの記述で'compile'となっている部分は、現在は'implementation'に変更されています。'compile'と記述するとgradleの同期で失敗するので気を付けましょう。

f:id:kdn-1017-wttd:20180710070232p:plain

f:id:kdn-1017-wttd:20180710070250p:plain

これで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などを利用する方法のほうがいいのかもしれません。

 

参考

MLKit for Firebase触ってみた(0) -概要/事前準備編- – 海鮮丼の国

アプリ開発が怖いほど楽になる「Firebase」を徹底解説!【初心者向け】