2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

使ってみようML Kit

Last updated at Posted at 2021-03-03

これからの時代はエッジコンピューティング来るでしょと思い、
まずは画像処理系のスキルを身につけるべくFirebaseのML Kitを触ってみたのでメモ。

ML Kitとは

Googleで開発されている機械学習を用いた機能を、モバイルアプリへ簡単に組み込むことができるSDK。
AndroidとiOS向けのライブラリが公開されている。
オンデバイスで動作するAPIとクラウドベースのAPIを利用することができる。
オンデバイスAPIを使えばネットワークの状態がなくても動作させることができる。
公式HP MLKit説明

ML Kitでできること

2021/02時点で以下の機能が公開されている。

機能 オンデバイス クラウド その他メモ
テキスト認識 日本語はクラウドのみ、オンデバイスはラテン文字の認識
顔検出
バーコードスキャン
画像のラベル付
オブジェクトの検出とトラッキング
ランドマーク認識
言語認識
翻訳
スマートリプライ 英語のみ対応、会話履歴を解析して回答案を作成する
AutoML推論
カスタム推論
Pose Detection(Beta) 人体の顔や手足の動きを追跡できる
Entity Extraction(Beta) テキストの表記から種別(住所、電話番号、日付、etc)を判定する

利用料金は?

オンデバイスは無料。
クラウドは有料。ただし、毎月1000リスクエストまで無料枠が設定されている。

実際にやってみた

以下の5つのインタフェースを検証してみた。

  1. テキスト認識(クラウド)
  2. テキスト認識(オンデバイス)
  3. バーコードスキャン
  4. 画像のラベル付け
  5. 翻訳(英語→日本語)

出力結果はこんな感じ

※最強の資格を持つ花子さん(免許センターのサンプル)と最近某家電量販店で価格調査したNASの写真(QRコードと1次元バーコード)を利用して検証。
qiita-square
qiita-square
qiita-square

1. テキスト認識(クラウド)

テキスト認識
FirebaseVisionImage visionImage = FirebaseVisionImage.fromBitmap(bitmap);
// Recognize Text   (Support Japanese)
FirebaseVisionDocumentTextRecognizer detector = FirebaseVision.getInstance().getCloudDocumentTextRecognizer();
Task<FirebaseVisionDocumentText> result = detector.processImage(visionImage)
    .addOnSuccessListener(new OnSuccessListener<FirebaseVisionDocumentText>() {
        @Override
        public void onSuccess(FirebaseVisionDocumentText result) {
            // Task completed successfully
            // TextBlock単位で情報取得。
            List<FirebaseVisionDocumentText.Block> blocks = result.getBlocks();
            ・・・中略・・・
            for (int i = 0; i < blocks.size(); i++) {
                // per Block
                FirebaseVisionDocumentText.Block block = blocks.get(i);
                // テキスト解析結果
                String recognizedText = block.getText();
                // blockの矩形情報を取得
                Rect rect = block.getBoundingBox();
                //corner top left
                String.format("(%d, %d)",rect.left, rect.top);
                //corner top right
                String.format("(%d, %d)",rect.right, rect.top);
                //corner bottom right
                String.format("(%d, %d)",rect.right, rect.bottom);
                //corner bottom left
                String.format("(%d, %d)",rect.left, rect.bottom);
            }

            try {
                detector.close(); //close処理
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    })
    .addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            ・・・中略・・・
        }
    });

どういう単位で区切られているかは掴めず。

2. テキスト認識(オンデバイス)

ラテン語は取得してくれるそうな。日本語は取れない。

テキスト認識(オンデバイス)
InputImage bundleImage = InputImage.fromBitmap(bitmap, 0);
// Recognize text https://developers.google.com/ml-kit/vision/text-recognition/android
// Non-support Japanese,Chinese,Korean. You want recognize that, you need to use CloudApi.
TextRecognizer recognizer = TextRecognition.getClient();
Task<Text> textRecognizedResult =
        recognizer.process(bundleImage)
                .addOnSuccessListener(new OnSuccessListener<Text>() {
                    @Override
                    public void onSuccess(Text visionText) {
                        // TextBlock単位で情報取得。
                        List<Text.TextBlock> blocks = visionText.getTextBlocks();
                        ・・・中略・・・
                        for (int i = 0; i < blocks.size(); i++) {
                            // per Block  Block要素はText.LineのListを持つのでList<Text.Line> lines = blocks.get(i).getLines();といった感じでLine単位での処理も可能
                            // Point配列は、0:左上、1:右上、2:右下、3:左下で構成される
                            Point[] point = blocks.get(i).getCornerPoints();
                            // pointはnullを許容するのでnullチェック
                            if (point != null) {
                              String recognizedText = blocks.get(i).getText();
                              //corner top left
                              String.format("(%d, %d)",point[0].x, point[0].y);
                              //corner top right
                              String.format("(%d, %d)",point[1].x, point[1].y);
                              //corner bottom right
                              String.format("(%d, %d)",point[2].x, point[2].y);
                              //corner bottom left
                              String.format("(%d, %d)",point[3].x, point[3].y);
                            }
                        }
                        recognizer.close();// close処理
                    }
                })
                .addOnFailureListener(
                        new OnFailureListener() {
                            @Override
                            public void onFailure(@NonNull Exception e) {
                                ・・・中略・・・
                            }
                        });

3. バーコードスキャン(オンデバイス)

ML KitのバーコードタイプがEnumで実装されていないので自作クラスを作成。
1ページに複数のバーコードがあってもとってくれる。

バーコードスキャン(オンデバイス)
// Scan barcode https://developers.google.com/ml-kit/vision/barcode-scanning/android#java
// set paramater
BarcodeScannerOptions options =
        new BarcodeScannerOptions.Builder()
                /**  スキャン対象のバーコードの型を設定しておくことが性能面で推奨されている。未指定の場合は自動判定してくれる。
                .setBarcodeFormats(
                        Barcode.FORMAT_QR_CODE, //QR Code
                        Barcode.FORMAT_CODABAR, //Codabar
                        Barcode.FORMAT_CODE_128,
                        Barcode.FORMAT_CODE_93,
                        Barcode.FORMAT_CODE_39,
                        Barcode.FORMAT_EAN_8, // JAN Code
                        Barcode.FORMAT_EAN_13) // JAN Code
                 **/
                .build();
BarcodeScanner scanner = BarcodeScanning.getClient(options);

Task<List<Barcode>> barcodeScanResult = scanner.process(bundleImage)
        .addOnSuccessListener(new OnSuccessListener<List<Barcode>>() {
            @Override
            public void onSuccess(List<Barcode> barcodes) {  //複数バーコードが取得可能
                for (int i = 0; i < barcodes.size(); i++) {
                    // per Block
                    Point[] point = barcodes.get(i).getCornerPoints();
                    if (point != null) {
                       String displayValue = barcodes.get(i).getDisplayValue();
                       String barcodeType = BarcodeType.getById(barcodes.get(i).getFormat()).getLabel();
                       ・・・中略Pointの取り方はTextと同じ・・・
                    }
                }
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                ・・・中略・・・
            }
        });

    // https://developers.google.com/android/reference/com/google/mlkit/vision/barcode/Barcode.BarcodeFormat
    public enum BarcodeType {
        FORMAT_UNKNOWN("FORMAT_UNKNOWN",-1),
        FORMAT_ALL_FORMATS("FORMAT_ALL_FORMATS",0),
        FORMAT_CODE_128("FORMAT_CODE_128",1),
        FORMAT_CODE_39("FORMAT_CODE_39",2),
        FORMAT_CODE_93("FORMAT_CODE_93",4),
        FORMAT_CODABAR("FORMAT_CODABAR",8),
        FORMAT_DATA_MATRIX("FORMAT_DATA_MATRIX",16),
        FORMAT_EAN_13("FORMAT_EAN_13",32),
        FORMAT_EAN_8("FORMAT_EAN_8",64),
        FORMAT_ITF("FORMAT_ITF",128),
        FORMAT_QR_CODE("FORMAT_QR_CODE",256),
        FORMAT_UPC_A("FORMAT_UPC_A",512),
        FORMAT_UPC_E("FORMAT_UPC_E",1024),
        FORMAT_PDF417("FORMAT_PDF417",2048),
        FORMAT_AZTEC("FORMAT_AZTEC",4096),
        TYPE_UNKNOWN("TYPE_UNKNOWN",0),
        TYPE_CONTACT_INFO("TYPE_CONTACT_INFO",1),
        TYPE_EMAIL("TYPE_EMAIL",2),
        TYPE_ISBN("TYPE_ISBN",3),
        TYPE_PHONE("TYPE_PHONE",4),
        TYPE_PRODUCT("TYPE_PRODUCT",5),
        TYPE_SMS("TYPE_SMS",6),
        TYPE_TEXT("TYPE_TEXT",7),
        TYPE_URL("TYPE_URL",8),
        TYPE_WIFI("TYPE_WIFI",9),
        TYPE_GEO("TYPE_GEO",10),
        TYPE_CALENDAR_EVENT("TYPE_CALENDAR_EVENT",11),
        TYPE_DRIVER_LICENSE("TYPE_DRIVER_LICENSE",12);

        private String label;
        private int id;

        private BarcodeType(String label, int id) {
            this.label = label;
            this.id = id;
        }

        public String getLabel() {
            return label;
        }

        public int getId() {
            return id;
        }

        public static BarcodeType getById(int id) {
            for( BarcodeType barcodeType : BarcodeType.values() ) {
                if( barcodeType.getId() == id ) {
                    return barcodeType;
                }
            }
            return null;
        }
    }

4. 画像ラベル付け(オンデバイス)

HumanとかChildとかPaperとかHandとか。色々ラベルつけてくれる。

画像ラベル付け(オンデバイス)
// Image labeling
ImageLabeler labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS);
labeler.process(bundleImage)
        .addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() {
            @Override
            public void onSuccess(List<ImageLabel> labels) { //複数のラベル取得
                // Task completed successfully
                for (int i = 0; i < labels.size(); i++) {
                    String label = labels.get(i).getText();
                    String confidence = String.format("%f",labels.get(i).getConfidence());
                }

                labeler.close(); //close処理
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                ・・・中略・・・
            }
        });

5. 翻訳(オンデバイス)

This is a pen. → これはペンです。 の翻訳結果は確認できた。

翻訳(オンデバイス)
// Create an English-JAPANESE translator:
TranslatorOptions translationOptions =
        new TranslatorOptions.Builder()
                .setSourceLanguage(TranslateLanguage.ENGLISH)
                .setTargetLanguage(TranslateLanguage.JAPANESE)
                .build();
final Translator englishJapaneseTranslator = Translation.getClient(translationOptions);

if (!hasDownlodTranslataMode) {
    DownloadConditions conditions = new DownloadConditions.Builder().requireWifi().build();
    englishJapaneseTranslator.downloadModelIfNeeded(conditions)
            .addOnSuccessListener(
                    new OnSuccessListener() {
                        @Override
                        public void onSuccess(Object o) {
                            // Model downloaded successfully. Okay to start translating. 30MBくらいの学習モデルを端末にダウンロードする処理が必要。サンプルなので変数に状態をセット。
                            hasDownlodTranslataMode = true;
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            // Model couldn’t be downloaded or other internal error.
                            // ...
                        }
                    });
}

// 学習モデルがダウンロード終わっていれば翻訳処理を実施
if (hasDownlodTranslataMode) {
    englishJapaneseTranslator.translate("This is a pen.")
            .addOnSuccessListener(
                    new OnSuccessListener() {
                        @Override
                        public void onSuccess(@NonNull Object result) {
                            // Translation successful.
                            String translatedText = (String) result; // 「これはペンです。」が出力される。
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            ・・・中略・・
                        }
                    });
}

Github URL

MLKitサンプルコード(Android)

感想

  • 実装は数行レベル。日本語の解析単位のクセを見抜くのがポイントかも。
  • 本当はやりたかった、「この矩形位置のOCR結果を返してくれる」は自前での実装が必要っぽい。
  • オンデバイスのテキスト解析は日本語未対応(翻訳→翻訳で無理やりできる??)
  • テキスト解析(クラウド)は日本語であってもそこそこ解析してくれる

参考

環境構築は以下のサイトを参考にさせていただきました。
ML Kit For Firebaseを使ってスマホで色々検出してみた

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?