Android'de ML Kit ile barkod tarama

Barkodları tanımak ve kodunu çözmek için ML Kit'i kullanabilirsiniz.

ÖzellikGruplandırılmamışGruplandırılanlar
UygulamaModel, Google Play Hizmetleri aracılığıyla dinamik olarak indirilir.Model, derleme sırasında statik olarak uygulamanıza bağlıdır.
Uygulama boyutuYaklaşık 200 KB boyut artışı.Yaklaşık 2,4 MB boyut artışı.
Başlatma süresiİlk kullanımdan önce modelin indirilmesini beklemeniz gerekebilir.Model hemen kullanılabilir.

Deneyin

Başlamadan önce

  1. Proje düzeyindeki build.gradle dosyanıza Google'ın Hem buildscript hem de allprojects bölümlerinizde Maven deposu.

  2. ML Kit Android kitaplıklarının bağımlılıklarını modülünüze uygulama düzeyinde gradle dosyasıdır. Bu dosya genellikle app/build.gradle boyutundadır. Şunlardan birini seçin: bağımlılıkları belirlemenize yardımcı olur:

    Modeli uygulamanızla gruplandırmak için:

    dependencies {
      // ...
      // Use this dependency to bundle the model with your app
      implementation 'com.google.mlkit:barcode-scanning:17.2.0'
    }
    

    Modeli Google Play Hizmetleri'nde kullanmak için:

    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.3.0'
    }
    
  3. Modeli Google Play Hizmetleri'nde kullanmayı seçerseniz yapılandırabilirsiniz. uygulamanız sonrasında modeli cihaza otomatik olarak indirecek Google Play Store'dan yüklenir. Bunu yapmak için aşağıdaki beyanı Uygulamanızın AndroidManifest.xml dosyası:

    <application ...>
          ...
          <meta-data
              android:name="com.google.mlkit.vision.DEPENDENCIES"
              android:value="barcode" >
          <!-- To use multiple models: android:value="barcode,model2,model3" -->
    </application>
    

    Ayrıca model kullanılabilirliğini açıkça kontrol edebilir ve Google Play Hizmetleri ModuleLoadClient API'si

    Yükleme zamanı modelinin indirilmesini etkinleştirmezseniz veya açıkça indirme isteğinde bulunmazsanız model, tarayıcıyı ilk çalıştırdığınızda indirilir. Yaptığınız istekler hiçbir sonuç döndürmez.

Giriş resmi kuralları

  • ML Kit'in barkodları doğru okuyabilmesi için giriş resimlerinde yeterli piksel verisiyle temsil edilen barkodlar.

    Belirli piksel verisi gereksinimleri hem çok sayıda barkod olduğundan, barkodla birlikte kodlanan veri miktarı taşımayı destekler. Genellikle anlamlı olan en küçük barkodun birimi en az 2 piksel genişliğinde olmalı ve 2 piksel yüksekliğinde 2 boyutlu kodlar.

    Örneğin, EAN-13 barkodları çubuklar ile boşluklardan oluşur. Bu boşlukların sayısı 1, 2, 3 veya 4 birim genişliğinde olduğundan EAN-13 barkod görüntüsünde ideal olarak çubuklar ve en az 2, 4, 6 ve 8 piksel genişliğinde boşluklar. Çünkü EAN-13 barkod toplam 95 birim genişliğinde, barkod en az 190 olmalıdır piksel genişliğinde.

    PDF417 gibi daha yoğun biçimler için güvenli okumasını sağlayın. Örneğin, bir PDF417 kodunda en fazla 34 17 birim genişliğinde "kelimeler" bir satır olacak şekilde ekleyebilirsiniz. Bu, tercihen 1156 piksel genişliğinde.

  • Kötü bir resim odağı, tarama doğruluğunu etkileyebilir. Uygulamanız kabul edilebilir sonuçlar almak için kullanıcıdan resmi yeniden çekmesini isteyin.

  • Tipik uygulamalarda, daha yüksek bir Barkod üreten, 1280x720 veya 1920x1080 gibi çözünürlüklü bir resim uzak bir mesafeden taranabilir niteliktedir.

    Ancak gecikmenin kritik olduğu uygulamalarda daha iyi daha düşük çözünürlükte çekerek daha iyi performans giriş resminin büyük kısmını barkod oluşturur. Şunlara da bakabilirsiniz: Gerçek zamanlı performansı iyileştirmeye yönelik ipuçları.

1. Barkod tarayıcıyı yapılandırın

Hangi barkod biçimlerini okumayı beklediğinizi bilirseniz hızı artırabilirsiniz otomatik olarak devre dışı bırakabilirsiniz.

Örneğin, yalnızca Aztek kodunu ve QR kodlarını tespit etmek için bir BarcodeScannerOptions nesnesini tanımlayın:

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();

Aşağıdaki biçimler desteklenir:

  • Kod 128 (FORMAT_CODE_128)
  • Kod 39 (FORMAT_CODE_39)
  • Kod 93 (FORMAT_CODE_93)
  • Kodabar (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 Kodu (FORMAT_QR_CODE)
  • PDF417 (FORMAT_PDF417)
  • Aztek (FORMAT_AZTEC)
  • Veri Matrisi (FORMAT_DATA_MATRIX)

Ayrıca, paket halinde sunulan 17.1.0 model ve paketlenmemiş model 18.2.0'dan itibaren enableAllPotentialBarcodes() çalıştırılsa bile tüm potansiyel barkodları döndürür. kodu çözülemiyor. Bu bilgi, daha fazla tespit edilmesini kolaylaştırmak için kullanılabilir. Örneğin, barkodun daha net bir görüntüsünü almak için kamerayı yakınlaştırarak sınırlayıcı kutudur.

Kotlin

val options = BarcodeScannerOptions.Builder()
        .setBarcodeFormats(...)
        .enableAllPotentialBarcodes() // Optional
        .build()

Java

BarcodeScannerOptions options =
        new BarcodeScannerOptions.Builder()
        .setBarcodeFormats(...)
        .enableAllPotentialBarcodes() // Optional
        .build();

Further on, starting from bundled library 17.2.0 and unbundled library 18.3.0, a new feature called auto-zoom has been introduced to further enhance the barcode scanning experience. With this feature enabled, the app is notified when all barcodes within the view are too distant for decoding. As a result, the app can effortlessly adjust the camera's zoom ratio to the recommended setting provided by the library, ensuring optimal focus and readability. This feature will significantly enhance the accuracy and success rate of barcode scanning, making it easier for apps to capture information precisely.

To enable auto-zooming and customize the experience, you can utilize the setZoomSuggestionOptions() method along with your own ZoomCallback handler and desired maximum zoom ratio, as demonstrated in the code below.

Kotlin

val options = BarcodeScannerOptions.Builder()
        .setBarcodeFormats(...)
        .setZoomSuggestionOptions(
            new ZoomSuggestionOptions.Builder(zoomCallback)
                .setMaxSupportedZoomRatio(maxSupportedZoomRatio)
                .build()) // Optional
        .build()

Java

BarcodeScannerOptions options =
        new BarcodeScannerOptions.Builder()
        .setBarcodeFormats(...)
        .setZoomSuggestionOptions(
            new ZoomSuggestionOptions.Builder(zoomCallback)
                .setMaxSupportedZoomRatio(maxSupportedZoomRatio)
                .build()) // Optional
        .build();

zoomCallback is required to be provided to handle whenever the library suggests a zoom should be performed and this callback will always be called on the main thread.

The following code snippet shows an example of defining a simple callback.

Kotlin

fun setZoom(ZoomRatio: Float): Boolean {
    if (camera.isClosed()) return false
    camera.getCameraControl().setZoomRatio(zoomRatio)
    return true
}

Java

boolean setZoom(float zoomRatio) {
    if (camera.isClosed()) {
        return false;
    }
    camera.getCameraControl().setZoomRatio(zoomRatio);
    return true;
}

maxSupportedZoomRatio is related to the camera hardware, and different camera libraries have different ways to fetch it (see the javadoc of the setter method). In case this is not provided, an unbounded zoom ratio might be produced by the library which might not be supported. Refer to the setMaxSupportedZoomRatio() method introduction to see how to get the max supported zoom ratio with different Camera libraries.

When auto-zooming is enabled and no barcodes are successfully decoded within the view, BarcodeScanner triggers your zoomCallback with the requested zoomRatio. If the callback correctly adjusts the camera to this zoomRatio, it is highly probable that the most centered potential barcode will be decoded and returned.

A barcode may remain undecodable even after a successful zoom-in. In such cases, BarcodeScanner may either invoke the callback for another round of zoom-in until the maxSupportedZoomRatio is reached, or provide an empty list (or a list containing potential barcodes that were not decoded, if enableAllPotentialBarcodes() was called) to the OnSuccessListener (which will be defined in step 4. Process the image).

2. Prepare the input image

To recognize barcodes in an image, create an InputImage 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
          // ...
        }
    }
}

Resmin dönme derecesini sağlayan bir kamera kitaplığı kullanmıyorsanız cihazın dönüş derecesinden ve kameranın yönünden hesaplayabilir cihazdaki sensör:

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;
}

Ardından, media.Image nesnesini ve döndürme derecesi değerini InputImage.fromMediaImage() değerine ayarlayın:

Kotlin

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

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

Dosya URI'si kullanarak

InputImage oluşturmak için uygulama bağlamını ve dosya URI'sini InputImage.fromFilePath(). Bu özellik, kullanıcıdan seçim yapmasını istemek için bir ACTION_GET_CONTENT niyeti kullanın galeri uygulamasından bir resim.

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 veya ByteArray kullanarak

InputImage oluşturmak için bir ByteBuffer veya ByteArray nesnesinden alıp almayacaksanız önce resmi hesaplayın media.Image girişi için daha önce açıklandığı gibi dönme derecesi. Ardından, arabellek veya diziyle InputImage nesnesini, bu resmin yükseklik, genişlik, renk kodlama biçimi ve döndürme derecesi:

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 kullanarak

InputImage oluşturmak için Bitmap nesnesindeki şu bildirimi yapın:

Kotlin

val image = InputImage.fromBitmap(bitmap, 0)

Java

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

Resim, döndürme dereceleriyle birlikte bir Bitmap nesnesiyle temsil edilir.

3. BarcodeScanner'ın bir örneğini alın

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. Resmi işleyin

Resmi process yöntemine geçirin:

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. Barkodlardan bilgi al

Barkod tanıma işlemi başarılı olursa Barcode listesi nesneler başarılı işleyiciye aktarılır. Her Barcode nesnesi temsil eder resimde algılanan barkod. Her barkod için giriş görüntüsündeki sınırlayıcı koordinatların yanı sıra barkodu görürsünüz. Ayrıca, barkod tarayıcısı veri türünü tarafından kodlanırsa ayrıştırılmış veri içeren bir nesne alabilirsiniz.

Örneğin:

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;
    }
}

Gerçek zamanlı performansı iyileştirmeye yönelik ipuçları

Barkodları gerçek zamanlı bir uygulamada taramak istiyorsanız aşağıdaki talimatları uygulayın:

  • Kameranın doğal çözünürlüğünde giriş yakalamayın. Bazı cihazlarda yerel çözünürlükte giriş yakalamak çok büyük (10+ megapiksel) resimler, hiçbir yararı olmadan çok düşük gecikme emin olun. Bunun yerine, kameradan yalnızca gerekli boyutu isteyin (genellikle 2 megapikselden fazla değildir).

    Tarama hızı önemliyse yakalanan görüntüyü daha da düşürebilirsiniz belirler. Ancak barkodla ilgili minimum boyut gereksinimlerini de göz önünde bulundurun. kontrol edin.

    Bir akış dizisindeki barkodları tanımaya çalışıyorsanız tanıyıcı, kareden kareye veya kareye çerçeve. Aynı sayıda ardışık düzeni elde edene kadar değerini belirlemektir.

    Denetim Toplamı basamağı, ITF ve CODE-39 için desteklenmiyor.

  • URL'yi Camera veya camera2 API, algılayıcıya yapılan çağrıları kısıtlamaz. Yeni bir video çerçeve, algılayıcı çalışırken kullanılabilir hale gelirse çerçeveyi bırakın. Bkz. Örnek için hızlı başlangıç örnek uygulamasındaki VisionProcessorBase sınıfı.
  • CameraX API'yi kullanıyorsanız karşı baskı stratejisinin varsayılan değerine ayarlandığından emin olun ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) Bu, aynı anda yalnızca bir resmin analiz için gönderilmesini garanti eder. Daha fazla resim üretilirse analiz aracı meşgulken üretilirse otomatik olarak bırakılır ve teslimat. Analiz edilen resim, çağırarak kapatıldıktan sonra ImageProxy.close(), bir sonraki en son resim gönderilir.
  • Algılayıcının çıkışını, üzerine grafik yerleştirmek için giriş görüntüsünü kullanın, önce ML Kit'ten sonucu alın ve ardından görüntüyü oluşturun tek bir adımda yapabilirsiniz. Bu, görüntü yüzeyine oluşturulur her giriş karesi için yalnızca bir kez. Bkz. CameraSourcePreview ve Hızlı başlangıç örnek uygulamasındaki GraphicOverlay sınıflarına göz atın.
  • Camera2 API'sini kullanıyorsanız görüntüleri şurada yakalayın: ImageFormat.YUV_420_888 biçimindedir. Eski Kamera API'sini kullanıyorsanız görüntüleri şurada yakalayın: ImageFormat.NV21 biçimindedir.