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

ML Kit を使用すると、画像や動画内のテキスト(たとえば、 あります。この機能の主な特徴は次のとおりです。

<ph type="x-smartling-placeholder">
機能 バンドルされていません バンドル
ライブラリ名 com.google.android.gms:play-services-mlkit-text-recognition

com.google.android.gms:play-services-mlkit-text-recognition-chinese

com.google.android.gms:play-services-mlkit-text-recognition-devanagari

com.google.android.gms:play-services-mlkit-text-recognition-japanese

com.google.android.gms:play-services-mlkit-text-recognition-korean

com.google.mlkit:text-recognition

com.google.mlkit:text-recognition-chinese

com.google.mlkit:text-recognition-devanagari

com.google.mlkit:text-recognition-japanese

com.google.mlkit:text-recognition-korean

実装 モデルは Google Play 開発者サービスを介して動的にダウンロードされます。 モデルは、ビルド時にアプリに静的にリンクされます。
アプリのサイズ スクリプト アーキテクチャごとに約 260 KB のサイズ増加。 アーキテクチャごとに、スクリプトごとに約 4 MB のサイズが増加します。
初期化時間 初めて使用するには、モデルがダウンロードされるのを待たなければならない場合があります。 モデルはすぐに使用できます。
パフォーマンス ラテン文字のスクリプト ライブラリはほとんどのデバイスでリアルタイム、その他のデバイスでは遅くなります。 ラテン文字のスクリプト ライブラリはほとんどのデバイスでリアルタイム、その他のデバイスでは遅くなります。

試してみる

始める前に

<ph type="x-smartling-placeholder">
  1. プロジェクト レベルの build.gradle ファイルで、buildscript セクションと allprojects セクションの両方に Google の Maven リポジトリを組み込みます。
  2. ML Kit Android ライブラリの依存関係をモジュールのアプリレベルの Gradle ファイル(通常は app/build.gradle)に追加します。

    モデルをアプリにバンドルする場合:

    dependencies {
      // To recognize Latin script
      implementation 'com.google.mlkit:text-recognition:16.0.0'
    
      // To recognize Chinese script
      implementation 'com.google.mlkit:text-recognition-chinese:16.0.0'
    
      // To recognize Devanagari script
      implementation 'com.google.mlkit:text-recognition-devanagari:16.0.0'
    
      // To recognize Japanese script
      implementation 'com.google.mlkit:text-recognition-japanese:16.0.0'
    
      // To recognize Korean script
      implementation 'com.google.mlkit:text-recognition-korean:16.0.0'
    }
    

    Google Play 開発者サービスでモデルを使用する場合:

    dependencies {
      // To recognize Latin script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition:19.0.0'
    
      // To recognize Chinese script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition-chinese:16.0.0'
    
      // To recognize Devanagari script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition-devanagari:16.0.0'
    
      // To recognize Japanese script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition-japanese:16.0.0'
    
      // To recognize Korean script
      implementation 'com.google.android.gms:play-services-mlkit-text-recognition-korean:16.0.0'
    }
    
  3. Google Play 開発者サービスでモデルを使用することを選択した場合、 自動的にモデルがデバイスにダウンロードされるように アプリが Google Play ストアからインストールされているかどうかを確認します。そのためには、次のコードを追加します。 宣言をアプリの AndroidManifest.xml ファイルに追加します。

    <application ...>
          ...
          <meta-data
              android:name="com.google.mlkit.vision.DEPENDENCIES"
              android:value="ocr" >
          <!-- To use multiple models: android:value="ocr,ocr_chinese,ocr_devanagari,ocr_japanese,ocr_korean,..." -->
    </application>
    

    モデルの提供状況を明示的に確認してダウンロードをリクエストすることもできます。 Google Play 開発者サービスの ModuleInstallClient API を使用する。install-time モデルを有効にしない場合 明示的なダウンロードをリクエストすると、モデルは最初に 設定されます。ダウンロードが完了する前に行ったリクエスト 結果が返されない。

1. TextRecognizer のインスタンスを作成する

TextRecognizer のインスタンスを作成し、オプションを渡します。 依存関係を宣言したライブラリに関連するコード:

Kotlin

// When using Latin script library
val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)

// When using Chinese script library
val recognizer = TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())

// When using Devanagari script library
val recognizer = TextRecognition.getClient(DevanagariTextRecognizerOptions.Builder().build())

// When using Japanese script library
val recognizer = TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().build())

// When using Korean script library
val recognizer = TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build())

Java

// When using Latin script library
TextRecognizer recognizer =
  TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS);

// When using Chinese script library
TextRecognizer recognizer =
  TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build());

// When using Devanagari script library
TextRecognizer recognizer =
  TextRecognition.getClient(new DevanagariTextRecognizerOptions.Builder().build());

// When using Japanese script library
TextRecognizer recognizer =
  TextRecognition.getClient(new JapaneseTextRecognizerOptions.Builder().build());

// When using Korean script library
TextRecognizer recognizer =
  TextRecognition.getClient(new KoreanTextRecognizerOptions.Builder().build());

2. 入力画像を準備する

画像内のテキストを認識するには、InputImage オブジェクトを作成します。 Bitmapmedia.ImageByteBuffer、バイト配列、または ダウンロードします次に、InputImage オブジェクトを TextRecognizerprocessImage メソッド。

InputImage を作成できます。 異なるソースからのオブジェクトについて、以下で説明します。

media.Image の使用

InputImage を作成するには: media.Image オブジェクトからオブジェクトをキャプチャします。たとえば、 渡すには、media.Image オブジェクトと画像の InputImage.fromMediaImage() に変更します。

「 <ph type="x-smartling-placeholder"></ph> CameraX ライブラリ、OnImageCapturedListenerImageAnalysis.Analyzer クラスが回転値を計算する できます。

Kotlin

private class YourImageAnalyzer : ImageAnalysis.Analyzer {

    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image
        if (mediaImage != null) {
            val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
            // Pass image to an ML Kit Vision API
            // ...
        }
    }
}

Java

private class YourAnalyzer implements ImageAnalysis.Analyzer {

    @Override
    public void analyze(ImageProxy imageProxy) {
        Image mediaImage = imageProxy.getImage();
        if (mediaImage != null) {
          InputImage image =
                InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees());
          // Pass image to an ML Kit Vision API
          // ...
        }
    }
}

画像の回転角度を取得するカメラ ライブラリを使用しない場合は、 デバイスの回転角度とカメラの向きから計算できます。 次の動作を行います。

Kotlin

private val ORIENTATIONS = SparseIntArray()

init {
    ORIENTATIONS.append(Surface.ROTATION_0, 0)
    ORIENTATIONS.append(Surface.ROTATION_90, 90)
    ORIENTATIONS.append(Surface.ROTATION_180, 180)
    ORIENTATIONS.append(Surface.ROTATION_270, 270)
}

/**
 * Get the angle by which an image must be rotated given the device's current
 * orientation.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Throws(CameraAccessException::class)
private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int {
    // 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.
    val deviceRotation = activity.windowManager.defaultDisplay.rotation
    var rotationCompensation = ORIENTATIONS.get(deviceRotation)

    // Get the device's sensor orientation.
    val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager
    val sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION)!!

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360
    }
    return rotationCompensation
}

Java

private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 0);
    ORIENTATIONS.append(Surface.ROTATION_90, 90);
    ORIENTATIONS.append(Surface.ROTATION_180, 180);
    ORIENTATIONS.append(Surface.ROTATION_270, 270);
}

/**
 * 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, boolean isFrontFacing)
        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);

    // Get the device's sensor orientation.
    CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE);
    int sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION);

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360;
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360;
    }
    return rotationCompensation;
}

次に、media.Image オブジェクトと 回転角度の値を InputImage.fromMediaImage() に設定する:

Kotlin

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

InputImage image = InputImage.fromMediaImage(mediaImage, rotation);

ファイル URI の使用

InputImage を作成するには: 渡すことにより、アプリのコンテキストとファイルの URI を InputImage.fromFilePath()。これは、 ACTION_GET_CONTENT インテントを使用してユーザーに選択を求める ギャラリーアプリから画像を作成できます

Kotlin

val image: InputImage
try {
    image = InputImage.fromFilePath(context, uri)
} catch (e: IOException) {
    e.printStackTrace()
}

Java

InputImage image;
try {
    image = InputImage.fromFilePath(context, uri);
} catch (IOException e) {
    e.printStackTrace();
}

ByteBuffer または ByteArray の使用

InputImage を作成するには: 作成するには、まず画像を計算してByteBufferByteArray 前述の media.Image 入力に対する回転角度。 次に、バッファまたは配列を含む InputImage オブジェクトを、画像の 高さ、幅、カラー エンコード形式、回転角度:

Kotlin

val image = InputImage.fromByteBuffer(
        byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)
// Or:
val image = InputImage.fromByteArray(
        byteArray,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)

Java

InputImage image = InputImage.fromByteBuffer(byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);
// Or:
InputImage image = InputImage.fromByteArray(
        byteArray,
        /* image width */480,
        /* image height */360,
        rotation,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);

Bitmap の使用

InputImage を作成するには: Bitmap オブジェクトから呼び出す場合は、次のように宣言します。

Kotlin

val image = InputImage.fromBitmap(bitmap, 0)

Java

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

画像は、Bitmap オブジェクトと回転角度で表されます。

3. 画像を処理する

画像を process メソッドに渡します。

Kotlin

val result = recognizer.process(image)
        .addOnSuccessListener { visionText ->
            // Task completed successfully
            // ...
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // ...
        }

Java

Task<Text> result =
        recognizer.process(image)
                .addOnSuccessListener(new OnSuccessListener<Text>() {
                    @Override
                    public void onSuccess(Text visionText) {
                        // Task completed successfully
                        // ...
                    }
                })
                .addOnFailureListener(
                        new OnFailureListener() {
                            @Override
                            public void onFailure(@NonNull Exception e) {
                                // Task failed with an exception
                                // ...
                            }
                        });
<ph type="x-smartling-placeholder">

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

テキスト認識オペレーションが成功すると、Text オブジェクトが 成功リスナー。Text オブジェクトには、 0 個以上の TextBlock オブジェクトです。

TextBlock は長方形のテキスト ブロックを表します。 これには 0 個以上の Line オブジェクトが含まれます。各 Line オブジェクトは、ゼロを含むテキスト行を表します。 または複数の Element オブジェクト。各 Element オブジェクトは、単語または単語に似たエンティティを表し、 Symbol オブジェクト。各 Symbol オブジェクトは、文字、数字、または単語に似たエンティティを表します。

TextBlockLineElementSymbol オブジェクトを使用すると、 領域内で認識されたテキスト、画像の境界座標、 その他の多くの属性(回転情報、信頼スコアなど) その他

例:

Kotlin

val resultText = result.text
for (block in result.textBlocks) {
    val blockText = block.text
    val blockCornerPoints = block.cornerPoints
    val blockFrame = block.boundingBox
    for (line in block.lines) {
        val lineText = line.text
        val lineCornerPoints = line.cornerPoints
        val lineFrame = line.boundingBox
        for (element in line.elements) {
            val elementText = element.text
            val elementCornerPoints = element.cornerPoints
            val elementFrame = element.boundingBox
        }
    }
}

Java

String resultText = result.getText();
for (Text.TextBlock block : result.getTextBlocks()) {
    String blockText = block.getText();
    Point[] blockCornerPoints = block.getCornerPoints();
    Rect blockFrame = block.getBoundingBox();
    for (Text.Line line : block.getLines()) {
        String lineText = line.getText();
        Point[] lineCornerPoints = line.getCornerPoints();
        Rect lineFrame = line.getBoundingBox();
        for (Text.Element element : line.getElements()) {
            String elementText = element.getText();
            Point[] elementCornerPoints = element.getCornerPoints();
            Rect elementFrame = element.getBoundingBox();
            for (Text.Symbol symbol : element.getSymbols()) {
                String symbolText = symbol.getText();
                Point[] symbolCornerPoints = symbol.getCornerPoints();
                Rect symbolFrame = symbol.getBoundingBox();
            }
        }
    }
}

入力画像のガイドライン

  • ML Kit でテキストを正確に認識するには、入力画像に 十分なピクセルデータで表現されます。次のことが理想的です。 各文字は少なくとも 16x16 ピクセルにする必要があります。一般的には、 24x24 ピクセルを超える場合は精度が向上します。

    そのため、たとえば名刺のスキャンには 640×480 の画像が適しています。 画像の幅いっぱいに表示されます。Google Pixel Tablet に印刷されたドキュメントをスキャンするには、 レターサイズの用紙を使用する場合は、720×1280 ピクセルの画像が必要になることがあります。

  • 画像のフォーカスが不適切だと、テキスト認識の精度に影響する可能性があります。あなたが ユーザーに画像をキャプチャし直すよう求めます。

  • リアルタイム アプリケーションでテキストを認識する場合は、 入力画像の全体的なサイズを考慮します。小 処理時間を短縮できます。レイテンシを短縮するには、スペースをできるだけ多く して、低解像度で画像をキャプチャします(精度は 上記の要件)。詳細については、次をご覧ください: 掲載結果を改善するためのヒント

パフォーマンスを向上させるためのヒント

  • Camera または camera2 API、 スロットリングするように構成されています。新しい動画が フレームが使用可能になる場合は、そのフレームをドロップします。詳しくは、 <ph type="x-smartling-placeholder"></ph> VisionProcessorBase クラスをご覧ください。
  • CameraX API を使用する場合は、 バックプレッシャー戦略がデフォルト値に ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST。 これにより、分析のために一度に 1 つの画像のみが配信されるようになります。もしより多くの画像が 生成された場合、自動的に破棄され、 提供します。次の呼び出しによって分析中の画像を閉じたら、 ImageProxy.close() が呼び出されると、次に最新の画像が配信されます。
  • 検出機能の出力を使用して、ディスプレイにグラフィックをオーバーレイする場合、 まず ML Kit から結果を取得してから、画像をレンダリングする 1 ステップでオーバーレイできますこれにより、ディスプレイ サーフェスにレンダリングされます。 入力フレームごとに 1 回だけです。詳しくは、 <ph type="x-smartling-placeholder"></ph> CameraSourcePreview および GraphicOverlay クラスをご覧ください。
  • Camera2 API を使用する場合は、 ImageFormat.YUV_420_888 形式。古い Camera API を使用する場合は、 ImageFormat.NV21 形式。
  • 解像度を下げて画像をキャプチャすることを検討してください。ただし この API の画像サイズの要件。