ML Kit を使用すると、バーコードの認識やデコードができます。
機能 | バンドルなし | バンドル |
---|---|---|
実装 | モデルは Google Play 開発者サービスを介して動的にダウンロードされます。 | モデルはビルド時に静的にアプリにリンクされます。 |
アプリのサイズ | 約 200 KB のサイズ増加。 | 約 2.4 MB のサイズ増大。 |
初期化時間 | モデルをダウンロードしてから最初の使用が必要になる場合があります。 | モデルはすぐに利用できます。 |
試してみる
- サンプルアプリを試して、この API の使用例をご覧ください。
- この API のエンドツーエンドの実装については、マテリアル デザインのショーケース アプリをご覧ください。
始める前に
プロジェクト レベルの
build.gradle
ファイルのbuildscript
セクションとallprojects
セクションの両方に Google の Maven リポジトリを組み込みます。ML Kit Android ライブラリの依存関係をモジュールのアプリレベルの Gradle ファイル(通常は
app/build.gradle
)に追加します。ニーズに応じて、次のいずれかの依存関係を選択します。モデルをアプリにバンドルする場合:
dependencies { // ... // Use this dependency to bundle the model with your app implementation 'com.google.mlkit:barcode-scanning:17.1.0' }
Google Play 開発者サービスでモデルを使用するには:
dependencies { // ... // Use this dependency to use the dynamically downloaded model in Google Play Services implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.2.0' }
Google Play 開発者サービスでモデルを使用する場合は、アプリが Play ストアからインストールされた後でデバイスに自動的にダウンロードされるようにアプリを構成できます。そのためには、アプリの
AndroidManifest.xml
ファイルに次の宣言を追加します。<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="barcode" > <!-- To use multiple models: android:value="barcode,model2,model3" --> </application>
モデルの可用性を明示的に確認したり、Google Play 開発者サービスの ModuleInstallClient API を使用してダウンロードをリクエストしたりすることもできます。
インストール時モデルのダウンロードを有効にしない場合や、明示的なダウンロードをリクエストしなかった場合は、スキャナの初回実行時にモデルがダウンロードされます。ダウンロードが完了する前にリクエストを送信しても、結果は生成されません。
入力画像に関するガイドライン
-
ML Kit でバーコードを正確に読み取るには、入力画像に含まれているバーコードが十分なピクセルデータによって表現されている必要があります。
多くのバーコードは可変サイズのペイロードをサポートしているため、特定のピクセルデータ要件は、バーコードの種類とエンコードされるデータの量によって異なります。一般に、バーコードの幅は 2 ピクセル以上にする必要があります。2 次元コードの場合は高さを 2 ピクセル以上にしてください。
たとえば、EAN-13 バーコードは幅が 1、2、3、4 のバーとスペースで構成されているため、EAN-13 バーコードの画像では、少なくとも 2、4、6、8 ピクセルのバーとスペースを使用することが理想的です。EAN-13 バーコードの幅は合計で 95 単位になるため、バーコードの幅は 190 ピクセル以上にする必要があります。
PDF417 のような高密度の形式を使用すると、ML Kit で読み取り精度を高めるため、より大きなピクセルサイズが必要になります。たとえば、PDF417 コードは 1 行に最大 34 個の 17 単位幅のワードを入れることができるため、1,156 ピクセル以上の幅が理想的です。
-
画像がぼやけていると、スキャン精度に影響する可能性があります。アプリが満足のいく結果を得られない場合は、ユーザーに画像をキャプチャし直すよう求めてください。
-
一般的なアプリケーションでは、カメラから遠く離れた位置からバーコードをスキャンできる、1280x720 や 1920x1080 などの高解像度の画像を使用することをおすすめします。
ただし、レイテンシが重要なアプリケーションでは、低解像度で画像をキャプチャすることでパフォーマンスを改善できますが、入力画像の大部分がバーコードである必要があります。リアルタイムのパフォーマンスを改善するためのヒントもご覧ください。
1. バーコード スキャナの設定
読み取るバーコード形式がわかっている場合は、その形式のみを検出するように構成して、バーコード検出器の速度を向上させることができます。たとえば、Aztec コードと QR コードのみを検出するには、次の例のように BarcodeScannerOptions
オブジェクトをビルドします。
Kotlin
val options = BarcodeScannerOptions.Builder() .setBarcodeFormats( Barcode.FORMAT_QR_CODE, Barcode.FORMAT_AZTEC) .build()
Java
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder() .setBarcodeFormats( Barcode.FORMAT_QR_CODE, Barcode.FORMAT_AZTEC) .build();
次の形式がサポートされています。
- コード 128(
FORMAT_CODE_128
) - コード 39(
FORMAT_CODE_39
) - コード 93(
FORMAT_CODE_93
) - Codabar(
FORMAT_CODABAR
) - EAN-13(
FORMAT_EAN_13
) - EAN-8(
FORMAT_EAN_8
) - ITF(
FORMAT_ITF
) - UPC-A(
FORMAT_UPC_A
) - UPC-E(
FORMAT_UPC_E
) - QR コード(
FORMAT_QR_CODE
) - PDF417(
FORMAT_PDF417
) - Aztec(
FORMAT_AZTEC
) - Data Matrix(
FORMAT_DATA_MATRIX
)
バンドルモデル 17.1.0 とバンドルされていないモデル 18.2.0 以降では、enableAllPotentialBarcodes()
を呼び出して、デコードできない可能性のあるすべてのバーコードを返すこともできます。これにより、検出を円滑に行えるようになります。たとえば、カメラを拡大縮小して、返された境界ボックス内のバーコードについてより鮮明な画像を取得できます。
Kotlin
val options = BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .enableAllPotentialBarcodes() // Optional .build()
Java
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .enableAllPotentialBarcodes() // Optional .build();
2. Prepare the input image
To recognize barcodes in an image, create anInputImage
object
from either a Bitmap
, media.Image
, ByteBuffer
, byte array, or a file on
the device. Then, pass the InputImage
object to the
BarcodeScanner
's process
method.
You can create an InputImage
object from different sources, each is explained below.
Using a media.Image
To create an InputImage
object from a media.Image
object, such as when you capture an image from a
device's camera, pass the media.Image
object and the image's
rotation to InputImage.fromMediaImage()
.
If you use the
CameraX library, the OnImageCapturedListener
and
ImageAnalysis.Analyzer
classes calculate the rotation value
for you.
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 から作成するには、アプリのコンテキストとファイルの 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
の使用
ByteBuffer
または ByteArray
から InputImage
オブジェクトを作成するには、まず、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
の使用
Bitmap
オブジェクトから InputImage
オブジェクトを作成するには、次の宣言を行います。
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
画像は Bitmap
オブジェクトと回転角度で表されます。
3.BarcodeScanner のインスタンスを取得する
Kotlin
val scanner = BarcodeScanning.getClient() // Or, to specify the formats to recognize: // val scanner = BarcodeScanning.getClient(options)
Java
BarcodeScanner scanner = BarcodeScanning.getClient(); // Or, to specify the formats to recognize: // BarcodeScanner scanner = BarcodeScanning.getClient(options);
4. 画像を処理する
画像をprocess
メソッドに渡します。
Kotlin
val result = scanner.process(image) .addOnSuccessListener { barcodes -> // Task completed successfully // ... } .addOnFailureListener { // Task failed with an exception // ... }
Java
Task<List<Barcode>> result = scanner.process(image) .addOnSuccessListener(new OnSuccessListener<List<Barcode>>() { @Override public void onSuccess(List<Barcode> barcodes) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
5. バーコードから情報を取得する
バーコード認識オペレーションが成功すると、Barcode
オブジェクトのリストが成功リスナーに渡されます。各 Barcode
オブジェクトは画像内で検出されたバーコードを表します。バーコードごとに、入力画像の境界座標と、バーコードによってエンコードされた元データを取得できます。また、バーコード スキャナがバーコードによってエンコードされたデータのタイプを判別できた場合は、解析されたデータを含むオブジェクトも取得できます。
例:
Kotlin
for (barcode in barcodes) { val bounds = barcode.boundingBox val corners = barcode.cornerPoints val rawValue = barcode.rawValue val valueType = barcode.valueType // See API reference for complete list of supported types when (valueType) { Barcode.TYPE_WIFI -> { val ssid = barcode.wifi!!.ssid val password = barcode.wifi!!.password val type = barcode.wifi!!.encryptionType } Barcode.TYPE_URL -> { val title = barcode.url!!.title val url = barcode.url!!.url } } }
Java
for (Barcode barcode: barcodes) { Rect bounds = barcode.getBoundingBox(); Point[] corners = barcode.getCornerPoints(); String rawValue = barcode.getRawValue(); int valueType = barcode.getValueType(); // See API reference for complete list of supported types switch (valueType) { case Barcode.TYPE_WIFI: String ssid = barcode.getWifi().getSsid(); String password = barcode.getWifi().getPassword(); int type = barcode.getWifi().getEncryptionType(); break; case Barcode.TYPE_URL: String title = barcode.getUrl().getTitle(); String url = barcode.getUrl().getUrl(); break; } }
リアルタイムのパフォーマンスを改善するためのヒント
リアルタイムのアプリケーションでバーコードをスキャンする場合は、適切なフレームレートを得るために次のガイドラインに従ってください。
-
カメラのネイティブ解像度で入力をキャプチャしないでください。一部のデバイスでは、ネイティブ解像度で入力をキャプチャすると、非常に大きい(10 メガピクセル以上の)画像が生成されます。レイテンシが非常に低くなるだけで精度が向上するわけではありません。代わりに、バーコード検出に必要なサイズをカメラからリクエストしてください。サイズは通常 2 メガピクセル以下です。
スキャン速度が重要な場合は、画像キャプチャの解像度をさらに下げることができます。ただし、上記の最小バーコード サイズの要件に注意してください。
ストリーミング動画フレームのシーケンスからバーコードを認識しようとすると、認識機能によってフレームごとに異なる結果が生成されます。同じ値が連続して返されるまで待ってから、適切な結果が返されることを確認します。
チェックサム ディジットは ITF と CODE-39 ではサポートされていません。
Camera
またはcamera2
API を使用する場合、検出器の呼び出しのスロットリングを行います。検出器の実行中に新しい動画フレームが使用可能になった場合は、そのフレームをドロップします。例については、クイックスタート サンプルアプリのVisionProcessorBase
クラスをご覧ください。CameraX
API を使用する場合は、バックプレッシャー戦略がデフォルト値ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
に設定されていることを確認します。 一度に 1 つのイメージのみが分析のために配信されることが保証されます。アナライザがビジー状態のときにさらに生成されるイメージは、自動的に破棄され、配信のキューに入りません。分析されている画像を ImageProxy.close() で閉じると、次に新しいイメージが配信されます。- 検出器の出力を使用して入力画像の上にグラフィックスをオーバーレイする場合は、まず ML Kit から検出結果を取得し、画像とオーバーレイを 1 つのステップでレンダリングします。これにより、ディスプレイ サーフェスへのレンダリングは入力フレームごとに 1 回で済みます。例については、クイックスタート サンプルアプリの
CameraSourcePreview
クラスとGraphicOverlay
クラスをご覧ください。 - Camera2 API を使用する場合は、
ImageFormat.YUV_420_888
形式で画像をキャプチャします。古い Camera API を使用する場合は、ImageFormat.NV21
形式で画像をキャプチャします。