ML Kit Text Recognition v2で日本語認識Androidアプリ

こんにちは。短い内容で恐縮ですが、よろしくお願いします。画像が多いだけです。

# やってみたこと

- [ML Kit Text Recognition v2](https://developers.google.com/ml-kit/vision/text-recognition/v2
)を使って
- カメラアプリで撮った日本語の字をOCRっぽく認識する
- Androidアプリを作ってみました。

## NIVEA Moisture Lip

唇が渇きやすい私にとっての日用必需品。

![NIVEA.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100612/8a0769b3-1915-e481-6ef8-e709a4174ed9.png)

「UNSCENTED」はどこから？と探してみたら、本体にありました。
「湿」が出ませんでした、残念。「濃」も「氵（さんずい）」が取れちゃってますね。
「ホホバオイル」も「ホfオイル」ですね、残念。
いやいや、それでも縦書きなのに認識したことがすごいです！
でも、「敏感な唇にもやさしい潤い感」は完全にスルーですね。左上の「薬用」も。

## ピスタチオ香るチョコレート

100均ショップDAISOに行ったら、なんと！皆さんご存じショコラメゾン「[ミュゼ・ドゥ・ショコラ・テオブロマ](https://www.theobroma.co.jp/)」の、かの有名なオーナーショコラティエの土屋公二シェフ（「ミスターカカオ」「味覚の魔術師」の異名を持ってます）のチョコが！チョコレート大好きな私はもうそれだけでテンション上がりました！

![チョコレート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100612/56172e54-ecb5-5ef8-9532-ed5895e75e6f.png)

ほぼ完璧！
「－スト3AP」は何だろう？「土屋公二シェフ監修」の部分かな？
ちなみにカメラで撮影するとき、カメラが自動で土屋公二シェフの顔にピントを合わせようとしたので、ちょっと可笑しかったです。お菓子買ったです。

## 自分の手書き

書いてるそばから「阝（おおざと）が甘いかな～」とは思っていたんです。

![東京都.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100612/26afd9a5-b5d8-870a-d271-99874d5bf925.png)

ちなみに、もう一度試行したら「東京券」となりました。はやり「阝」はしっかり大きく書けばよかったと後悔です。

# ML Kit Text Recognition v2

https://developers.google.com/ml-kit/guides

テキスト認識だけでなく、顔検出や、バーコード読み取りなどできるそうです。
ここではテキスト認識だけを試してみました。

## 日本語などサポートするようになった

v2より前は、ラテン文字しかテキスト認識できませんでした。
ですが、v2になってからは、中国語、デバナーガリー語、日本語、韓国語をサポートしたそうです。待ってました！ただし現時点ではまだベータ版です。

## オンデバイスで利用可能

https://developers.google.com/ml-kit/vision/text-recognition/v2

クラウドで解析をしてもらう、というのではなく、なんと端末内で完結できるのがすごいところです。
AndroidとiOSをサポートしてます。

# 開発

Windows 10、Android Studio 2021.2.1です。
端末はASUS製の古いスマホを使いました。

## ライブラリー

build.gradleに以下を追記してsync nowです。日本語のだけを取り寄せました。

```groovy:build.gradle
implementation 'com.google.mlkit:text-recognition-japanese:16.0.0-beta6'
```

## インストール済み標準カメラアプリを利用

自分でカメラ機能を実装するのは面倒くさかったので、インストール済み標準カメラアプリを利用することにします。

Manifestに以下のパーミッションなどを設定します。

```xml:AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />

<queries>
    <intent>
        <action android:name="android.media.action.IMAGE_CAPTURE" />
    </intent>
</queries>
```

Activityでは、インストール済みカメラアプリを利用するための`ActivityResultLauncher`をフィールドとして定義します。

## プログラム

Activity１個と、そのレイアウトリソースファイル１枚だけです。

前掲のスクリーンショットでも見えてますが、画面の大半を`<ImageView android:id="@+id/imageFromCamera">`、そして画面の下に`<Button android:id="@+id/btnInvokeCamera">`を配置しています。

CAMERAパーミッションのリクエストダイアログの対応のコードは、この記事の本題と乖離があるので割愛しています。

```java:MainActivity.java
public class MainActivity extends AppCompatActivity {

private final ActivityResultLauncher<Void> cameraLauncher = registerForActivityResult(
    new ActivityResultContracts.TakePicturePreview(), bitmap -> {
        findViewById(R.id.imageFromCamera).setImageBitmap(bitmap);

        int rotationDegree = 0;
        InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);

        // 日本語もサポートした！
        TextRecognizer recognizer = TextRecognition.getClient(new JapaneseTextRecognizerOptions.Builder().build());

        Task<Text> task = recognizer.process(image)
                .addOnSuccessListener(new OnSuccessListener<Text>() {
                    @Override
                    public void onSuccess(Text visionText) {
                        String resultText = visionText.getText(); // 文字列取得！
                        new AlertDialog.Builder(MainActivity.this).setMessage(resultText).show();
                    }
                });
         });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // ボタン押下でカメラアプリを起動
        findViewById(R.id.btnInvokeCamera).setOnClickListener(v -> cameraLauncher.launch(null));
    }
}
```

カメラ起動Launcherから返された`Bitmap`を、`com.google.mlkit.vision.common.InputImage`のコンストラクタに渡してインスタンス化します。

`com.google.mlkit.vision.text.TextRecognizer`インスタンスは、staticメソッド`getClient`から取得します。
今回は日本語で試したかったので、`JapaneseTextRecognizerOptions`オブジェクトを引数に渡しています（もしラテン文字だけでいい、というのであれば、名前付き定数`TextRecognizerOptions.DEFAULT_OPTIONS`です）。

`TextRecognizer`のメソッド`process`に引数`InputImage`を渡して認識してもらいます。
今回は成功したときだけしか実装しました。（`addOnSuccessListener`）。失敗したときの処理を実装したければ、`addOnFailureListener`メソッドも呼び出します（今回は省略しました）。

`OnSuccessListener`は関数型インタフェースですので、ラムダ式で書いた方が短く済みますね。

あとは`AlertDialog`にて認識結果の`String`を出力させているだけです。

# 最後に

いつの間にかML Kitが日本語に対応していました。すごいです。
次回はデバナーガリー語に挑戦したいと、思いま…否、ところでデバナーガリー語とは？？？
ということで、
[क्रिसमस की बधाई!](https://translate.google.co.jp/?hl=ja&sl=hi&tl=ja&text=%E0%A4%95%E0%A5%8D%E0%A4%B0%E0%A4%BF%E0%A4%B8%E0%A4%AE%E0%A4%B8%20%E0%A4%95%E0%A5%80%20%E0%A4%AC%E0%A4%A7%E0%A4%BE%E0%A4%88!&op=translate)

以上です。