ML Kit を使用して画像内のテキストを認識する(Android)

ML Kit を使用すると、画像内のテキストを認識できます。ML Kit には、画像内のテキスト(道路標識のテキストなど)を認識するのに適した汎用 API と、ドキュメントのテキストを認識するように最適化された API があります。汎用 API には、端末モデルとクラウドベース モデルの 2 つがあります。ドキュメント テキスト認識はクラウドベース モデルでのみ使用できます。クラウドモデルと端末モデルの比較については、概要をご覧ください。

この API の使用例については、GitHub の ML Kit クイックスタート サンプルを参照するか、Codelab を試してみてください。

準備

  1. まだアプリに Firebase を追加していない場合は、スタートガイドの手順に沿って追加してください。
  2. アプリレベルの build.gradle ファイルに ML Kit の依存関係を含めます。
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-vision:17.0.0'
    }
    
  3. 省略可能、ただし推奨: 端末用 API を使用する場合は、アプリが Play ストアからインストールされたら自動で ML モデルを端末にダウンロードするようにアプリを構成します。

    この構成を行うには、アプリの AndroidManifest.xml ファイルに次の宣言を追加します。

    <application ...>
      ...
      <meta-data
          android:name="com.google.firebase.ml.vision.DEPENDENCIES"
          android:value="ocr" />
      <!-- To use multiple models: android:value="ocr,model2,model3" -->
    </application>
    
    インストール時点でのモデルのダウンロードを有効にしない場合は、端末上の検出器の初回実行時にモデルがダウンロードされます。ダウンロードが完了する前にリクエストしても結果は生成されません。
  4. クラウドベースのモデルを使用するが、プロジェクトを Blaze プランにアップグレードしていない場合は、Firebase コンソールでアップグレードしてください。Blaze レベルのプロジェクトのみが Cloud Vision API を使用できます。
  5. クラウドベース モデルを使用する場合は、Cloud Vision API も有効にします。
    1. Cloud Console API ライブラリで Cloud Vision API を開きます。
    2. ページ上部のメニューで Firebase プロジェクトが選択されていることを確認します。
    3. API がまだ有効になっていない場合は、[有効にする] をクリックします。
    端末モデルのみを使用する場合は、この手順を省略できます。

これで、画像内のテキストを認識する準備ができました。


画像内のテキストを認識する

端末モデルまたはクラウドベース モデルを使用して画像内のテキストを認識するには、以下で説明するようにテキスト認識機能を実行します。

1. テキスト認識機能を実行する

画像内のテキストを認識するには FirebaseVisionImage オブジェクトを作成します。このオブジェクトは、Bitmapmedia.ImageByteBuffer、バイト配列、端末上のファイルのいずれかから作成できます。次に、FirebaseVisionImage オブジェクトを FirebaseVisionTextRecognizerprocessImage メソッドに渡します。

  1. 画像から FirebaseVisionImage オブジェクトを作成します。

    • FirebaseVisionImage オブジェクトを Bitmap オブジェクトから作成するコードは、以下のとおりです。
      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
      Bitmap オブジェクトによって表される画像は、これ以上回転させる必要がないように、正しい向きになっている必要があります。
    • FirebaseVisionImage オブジェクトを media.Image オブジェクトから作成するには(端末のカメラから画像をキャプチャする場合など)、端末の回転と搭載されたカメラセンサーの向きの両方を補正するために、まず画像の回転角度を決定します。
      private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
      static {
          ORIENTATIONS.append(Surface.ROTATION_0, 90);
          ORIENTATIONS.append(Surface.ROTATION_90, 0);
          ORIENTATIONS.append(Surface.ROTATION_180, 270);
          ORIENTATIONS.append(Surface.ROTATION_270, 180);
      }
      
      /**
       * Get the angle by which an image must be rotated given the device's current
       * orientation.
       */
      @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      private int getRotationCompensation(String cameraId, Activity activity, Context context)
              throws CameraAccessException {
          // Get the device's current rotation relative to its "native" orientation.
          // Then, from the ORIENTATIONS table, look up the angle the image must be
          // rotated to compensate for the device's rotation.
          int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
          int rotationCompensation = ORIENTATIONS.get(deviceRotation);
      
          // On most devices, the sensor orientation is 90 degrees, but for some
          // devices it is 270 degrees. For devices with a sensor orientation of
          // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
          CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
          int sensorOrientation = cameraManager
                  .getCameraCharacteristics(cameraId)
                  .get(CameraCharacteristics.SENSOR_ORIENTATION);
          rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
      
          // Return the corresponding FirebaseVisionImageMetadata rotation value.
          int result;
          switch (rotationCompensation) {
              case 0:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  break;
              case 90:
                  result = FirebaseVisionImageMetadata.ROTATION_90;
                  break;
              case 180:
                  result = FirebaseVisionImageMetadata.ROTATION_180;
                  break;
              case 270:
                  result = FirebaseVisionImageMetadata.ROTATION_270;
                  break;
              default:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  Log.e(TAG, "Bad rotation value: " + rotationCompensation);
          }
          return result;
      }

      次に、media.Image オブジェクトと回転値を FirebaseVisionImage.fromMediaImage() に渡します。

      FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
    • FirebaseVisionImage オブジェクトを ByteBuffer またはバイト配列から作成するには、前述のようにまず画像の回転を計算します。

      次に、画像の高さ、幅、カラー エンコーディング形式、回転を含む FirebaseVisionImageMetadata オブジェクトを作成します。

      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
              .setWidth(1280)
              .setHeight(720)
              .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
              .setRotation(rotation)
              .build();

      バッファまたは配列、およびメタデータ オブジェクトを使用して、FirebaseVisionImage オブジェクトを作成します。

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
      
    • FirebaseVisionImage オブジェクトをファイルから作成するには、アプリケーション コンテキストとファイルの URI を FirebaseVisionImage.fromFilePath() に渡します。
      FirebaseVisionImage image;
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e.printStackTrace();
      }

  2. FirebaseVisionTextRecognizer のインスタンスを取得します。

    端末モデルを使用するには:

    FirebaseVisionTextRecognizer textRecognizer = FirebaseVision.getInstance()
        .getOnDeviceTextRecognizer();
    

    クラウドベース モデルを使用するには:

    FirebaseVisionTextRecognizer textRecognizer = FirebaseVision.getInstance()
            .getCloudTextRecognizer();
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    FirebaseVisionCloudTextRecognizerOptions options =
            new FirebaseVisionCloudTextRecognizerOptions.Builder()
                    .setLanguageHints(Arrays.asList("en", "hi"))
                    .build();
    FirebaseVisionTextRecognizer textRecognizer = FirebaseVision.getInstance()
            .getCloudTextRecognizer(options);
    
  3. 最後に、画像を processImage メソッドに渡します。

    textRecognizer.processImage(image)
            .addOnSuccessListener(new OnSuccessListener<FirebaseVisionText>() {
                @Override
                public void onSuccess(FirebaseVisionText result) {
                    // Task completed successfully
                    // ...
                }
            })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            // Task failed with an exception
                            // ...
                        }
                    });
    

2. 認識したテキストのブロックからテキストを抽出する

テキスト認識オペレーションが成功すると、FirebaseVisionText オブジェクトが成功リスナーに渡されます。FirebaseVisionText オブジェクトには、画像で認識された全テキストと、0 個以上の TextBlock オブジェクトが含まれています。

TextBlock はテキストの長方形ブロックを表し、これには 0 個以上の Line オブジェクトが含まれます。各 Line オブジェクトには 0 個以上の Element オブジェクトが含まれ、これは単語や単語に似たエンティティ(日付や数字など)を表します。

TextBlockLineElement のそれぞれのオブジェクトについて、領域内で認識されたテキストと、領域の境界座標を取得できます。

例:

String resultText = result.getText();
for (TextBlock block: result.getTextBlocks()) {
    String blockText = block.getText();
    Float blockConfidence = block.getConfidence();
    List<RecognizedLanguage> blockLanguages = block.getRecognizedLanguages();
    Point[] blockCornerPoints = block.getCornerPoints();
    Rect blockFrame = block.getBoundingBox();
    for (Line line: block.getLines()) {
        String lineText = line.getText();
        Float lineConfidence = line.getConfidence();
        List<RecognizedLanguage> lineLanguages = line.getRecognizedLanguages();
        Point[] lineCornerPoints = line.getCornerPoints();
        Rect lineFrame = line.getBoundingBox();
        for (Element element: line.getElements()) {
            String elementText = element.getText();
            Float elementConfidence = element.getConfidence();
            List<RecognizedLanguage> elementLanguages = element.getRecognizedLanguages();
            Point[] elementCornerPoints = element.getCornerPoints();
            Rect elementFrame = element.getBoundingBox();
        }
    }
}

ドキュメントの画像でテキストを認識する

ドキュメントのテキストを認識するには、以下の説明に従ってクラウドベースのドキュメント テキスト認識機能を構成して実行します。

以下で説明するように、ドキュメント テキスト認識 API はドキュメントの画像を処理するための便利なインターフェースを提供します。ただし、FirebaseVisionTextRecognizer API で提供されるインターフェースを使用したい場合は、それを使用してドキュメントをスキャンすることもできます。これを行うには、高密度テキストモデルを使用するようにクラウド テキスト認識機能を構成します。

ドキュメント テキスト認識 API を使用するには:

1. テキスト認識機能を実行する

画像内のテキストを認識するには FirebaseVisionImage オブジェクトを作成します。このオブジェクトは、Bitmapmedia.ImageByteBuffer、バイト配列、端末上のファイルのいずれかから作成できます。次に、FirebaseVisionImage オブジェクトを FirebaseVisionDocumentTextRecognizerprocessImage メソッドに渡します。

  1. 画像から FirebaseVisionImage オブジェクトを作成します。

    • FirebaseVisionImage オブジェクトを Bitmap オブジェクトから作成するコードは、以下のとおりです。
      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
      Bitmap オブジェクトによって表される画像は、これ以上回転させる必要がないように、正しい向きになっている必要があります。
    • FirebaseVisionImage オブジェクトを media.Image オブジェクトから作成するには(端末のカメラから画像をキャプチャする場合など)、端末の回転と搭載されたカメラセンサーの向きの両方を補正するために、まず画像の回転角度を決定します。
      private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
      static {
          ORIENTATIONS.append(Surface.ROTATION_0, 90);
          ORIENTATIONS.append(Surface.ROTATION_90, 0);
          ORIENTATIONS.append(Surface.ROTATION_180, 270);
          ORIENTATIONS.append(Surface.ROTATION_270, 180);
      }
      
      /**
       * Get the angle by which an image must be rotated given the device's current
       * orientation.
       */
      @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      private int getRotationCompensation(String cameraId, Activity activity, Context context)
              throws CameraAccessException {
          // Get the device's current rotation relative to its "native" orientation.
          // Then, from the ORIENTATIONS table, look up the angle the image must be
          // rotated to compensate for the device's rotation.
          int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
          int rotationCompensation = ORIENTATIONS.get(deviceRotation);
      
          // On most devices, the sensor orientation is 90 degrees, but for some
          // devices it is 270 degrees. For devices with a sensor orientation of
          // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
          CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
          int sensorOrientation = cameraManager
                  .getCameraCharacteristics(cameraId)
                  .get(CameraCharacteristics.SENSOR_ORIENTATION);
          rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
      
          // Return the corresponding FirebaseVisionImageMetadata rotation value.
          int result;
          switch (rotationCompensation) {
              case 0:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  break;
              case 90:
                  result = FirebaseVisionImageMetadata.ROTATION_90;
                  break;
              case 180:
                  result = FirebaseVisionImageMetadata.ROTATION_180;
                  break;
              case 270:
                  result = FirebaseVisionImageMetadata.ROTATION_270;
                  break;
              default:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  Log.e(TAG, "Bad rotation value: " + rotationCompensation);
          }
          return result;
      }

      次に、media.Image オブジェクトと回転値を FirebaseVisionImage.fromMediaImage() に渡します。

      FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
    • FirebaseVisionImage オブジェクトを ByteBuffer またはバイト配列から作成するには、前述のようにまず画像の回転を計算します。

      次に、画像の高さ、幅、カラー エンコーディング形式、回転を含む FirebaseVisionImageMetadata オブジェクトを作成します。

      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
              .setWidth(1280)
              .setHeight(720)
              .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
              .setRotation(rotation)
              .build();

      バッファまたは配列、およびメタデータ オブジェクトを使用して、FirebaseVisionImage オブジェクトを作成します。

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
      
    • FirebaseVisionImage オブジェクトをファイルから作成するには、アプリケーション コンテキストとファイルの URI を FirebaseVisionImage.fromFilePath() に渡します。
      FirebaseVisionImage image;
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e.printStackTrace();
      }

  2. FirebaseVisionDocumentTextRecognizer のインスタンスを取得します。

    FirebaseVisionDocumentTextRecognizer textRecognizer = FirebaseVision.getInstance()
            .getCloudDocumentTextRecognizer();
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    FirebaseVisionCloudDocumentRecognizerOptions options =
            new FirebaseVisionCloudDocumentRecognizerOptions.Builder()
                    .setLanguageHints(Arrays.asList("en", "hi"))
                    .build();
    FirebaseVisionDocumentTextRecognizer textRecognizer = FirebaseVision.getInstance()
            .getCloudDocumentTextRecognizer(options);
    
  3. 最後に、画像を processImage メソッドに渡します。

    textRecognizer.processImage(image)
            .addOnSuccessListener(new OnSuccessListener<FirebaseVisionDocumentText>() {
                @Override
                public void onSuccess(FirebaseVisionDocumentText result) {
                    // Task completed successfully
                    // ...
                }
            })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            // Task failed with an exception
                            // ...
                        }
                    });
    

2. 認識したテキストのブロックからテキストを抽出する

テキスト認識オペレーションが成功すると、FirebaseVisionDocumentText オブジェクトが返されます。FirebaseVisionDocumentText オブジェクトには、画像で認識された全テキストと、認識されたドキュメントの構造が反映されているオブジェクトの階層が含まれます。

BlockParagraphWordSymbol のそれぞれのオブジェクトについて、領域内で認識されたテキストと、領域の境界座標を取得できます。

例:

String resultText = result.getText();
for (Block block: result.getBlocks()) {
    String blockText = block.getText();
    Float blockConfidence = block.getConfidence();
    List<RecognizedLanguage> blockRecognizedLanguages = block.getRecognizedLanguages();
    Rect blockFrame = block.getBoundingBox();
    for (Paragraph paragraph: block.getParagraphs()) {
        String paragraphText = paragraph.getText();
        Float paragraphConfidence = paragraph.getConfidence();
        List<RecognizedLanguage> paragraphRecognizedLanguages = paragraph.getRecognizedLanguages();
        Rect paragraphFrame = paragraph.getBoundingBox();
        for (Word word: paragraph.getWords()) {
            String wordText = word.getText();
            Float wordConfidence = word.getConfidence();
            List<RecognizedLanguage> wordRecognizedLanguages = word.getRecognizedLanguages();
            Rect wordFrame = word.getBoundingBox();
            for (Symbol symbol: word.getSymbols()) {
                String symbolText = symbol.getText();
                Float symbolConfidence = symbol.getConfidence();
                List<RecognizedLanguage> symbolRecognizedLanguages = symbol.getRecognizedLanguages();
                Rect symbolFrame = symbol.getBoundingBox();
            }
        }
    }
}

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。