Barkodları tanımak ve kodunu çözmek için ML Kit'i kullanabilirsiniz.
Başlamadan önce
- Henüz yapmadıysanız Firebase'i Android projenize ekleyin.
- Modül (uygulama düzeyinde) Gradle dosyanıza (genellikle
app/build.gradle
) ML Kit Android kitaplıkları için bağımlılıkları ekleyin:apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-vision:24.0.3' implementation 'com.google.firebase:firebase-ml-vision-barcode-model:16.0.1' }
Giriş resmi kuralları
-
ML Kit'in barkodları doğru bir şekilde okuyabilmesi için giriş resimleri, yeterli piksel verisiyle temsil edilen barkodlar içermelidir.
Belirli piksel verisi gereksinimleri hem barkodun türüne hem de barkodda kodlanan veri miktarına bağlıdır (çünkü çoğu barkod, değişken uzunlukta bir yükü desteklediğinden). Genel olarak barkodun en küçük anlamlı birimi en az 2 piksel genişliğinde (ve 2 boyutlu kodlar için 2 piksel yüksekliğinde) olmalıdır.
Örneğin, EAN-13 barkodları 1, 2, 3 veya 4 birim genişliğindeki çubuklar ve boşluklardan oluşur. Dolayısıyla EAN-13 barkod resminde ideal olarak en az 2, 4, 6 ve 8 piksel genişliğinde çubuklar ve boşluklar bulunur. EAN-13 barkodu toplamda 95 birim genişliğinde olduğundan barkod en az 190 piksel genişliğinde olmalıdır.
PDF417 gibi yoğun biçimler, ML Kit'in güvenilir bir şekilde okuyabilmesi için daha büyük piksel boyutlarına ihtiyaç duyar. Örneğin bir PDF417 kodu, tek bir satırda en fazla 34 17 birim genişliğinde "kelime" içerebilir. Bu boyut, ideal olarak en az 1.156 piksel genişliğinde olur.
-
Zayıf resim odağı, tarama doğruluğunu olumsuz etkileyebilir. Kabul edilebilir sonuçlar alamıyorsanız kullanıcıdan resmi yeniden çekmesini isteyin.
-
Tipik uygulamalarda, barkodların kameradan daha uzak bir mesafeden algılanabilmesini sağlayan daha yüksek çözünürlüklü bir resim (1280x720 veya 1920x1080 gibi) sağlanması önerilir.
Bununla birlikte, gecikmenin çok önemli olduğu uygulamalarda, resimleri daha düşük çözünürlükte yakalayarak performansı artırabilirsiniz. Bunun için barkodun giriş görüntüsünün büyük bir kısmını oluşturması gerekir. Gerçek zamanlı performansı iyileştirmeye yönelik ipuçları konusunu da inceleyin.
1. Barkod dedektörünü yapılandırma
Hangi barkod biçimlerini okumayı beklediğinizi biliyorsanız barkod dedektörünü yalnızca bu biçimleri algılayacak şekilde yapılandırarak hızını artırabilirsiniz.Örneğin, yalnızca Aztek kodunu ve QR kodlarını algılamak için aşağıdaki örnekte gösterildiği gibi bir FirebaseVisionBarcodeDetectorOptions
nesnesi oluşturun:
Java
FirebaseVisionBarcodeDetectorOptions options = new FirebaseVisionBarcodeDetectorOptions.Builder() .setBarcodeFormats( FirebaseVisionBarcode.FORMAT_QR_CODE, FirebaseVisionBarcode.FORMAT_AZTEC) .build();
Kotlin+KTX
val options = FirebaseVisionBarcodeDetectorOptions.Builder() .setBarcodeFormats( FirebaseVisionBarcode.FORMAT_QR_CODE, FirebaseVisionBarcode.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
)
2. Barkod dedektörünü çalıştırın
Bir görüntüdeki barkodları tanımak içinBitmap
, media.Image
, ByteBuffer
, bayt dizisi veya cihazdaki bir dosyadan FirebaseVisionImage
nesnesi oluşturun. Ardından FirebaseVisionImage
nesnesini FirebaseVisionBarcodeDetector
öğesinin detectInImage
yöntemine iletin.
Resminizden bir
FirebaseVisionImage
nesnesi oluşturun.-
media.Image
nesnesinden birFirebaseVisionImage
nesnesi oluşturmak için (örneğin, bir cihazın kamerasından resim çekerken)media.Image
nesnesini ve resmin dönüşünüFirebaseVisionImage.fromMediaImage()
yönüne geçirin.KameraX kitaplığını kullanırsanız
OnImageCapturedListener
veImageAnalysis.Analyzer
sınıfları rotasyon değerini sizin için hesaplar. Bu nedenle,FirebaseVisionImage.fromMediaImage()
işlevini çağırmadan önce rotasyonu, ML Kit'inROTATION_
sabit değerlerinden birine dönüştürmeniz yeterlidir:Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { private int degreesToFirebaseRotation(int degrees) { switch (degrees) { case 0: return FirebaseVisionImageMetadata.ROTATION_0; case 90: return FirebaseVisionImageMetadata.ROTATION_90; case 180: return FirebaseVisionImageMetadata.ROTATION_180; case 270: return FirebaseVisionImageMetadata.ROTATION_270; default: throw new IllegalArgumentException( "Rotation must be 0, 90, 180, or 270."); } } @Override public void analyze(ImageProxy imageProxy, int degrees) { if (imageProxy == null || imageProxy.getImage() == null) { return; } Image mediaImage = imageProxy.getImage(); int rotation = degreesToFirebaseRotation(degrees); FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation); // Pass image to an ML Kit Vision API // ... } }
Kotlin+KTX
private class YourImageAnalyzer : ImageAnalysis.Analyzer { private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) { 0 -> FirebaseVisionImageMetadata.ROTATION_0 90 -> FirebaseVisionImageMetadata.ROTATION_90 180 -> FirebaseVisionImageMetadata.ROTATION_180 270 -> FirebaseVisionImageMetadata.ROTATION_270 else -> throw Exception("Rotation must be 0, 90, 180, or 270.") } override fun analyze(imageProxy: ImageProxy?, degrees: Int) { val mediaImage = imageProxy?.image val imageRotation = degreesToFirebaseRotation(degrees) if (mediaImage != null) { val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation) // Pass image to an ML Kit Vision API // ... } } }
Resmin dönüşünü gösteren bir kamera kitaplığı kullanmıyorsanız cihazın dönüşüne ve cihazdaki kamera sensörünün yönüne göre hesaplama yapabilirsiniz:
Java
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; }
Kotlin+KTX
private val ORIENTATIONS = SparseIntArray() init { 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) @Throws(CameraAccessException::class) private fun getRotationCompensation(cameraId: String, activity: Activity, context: Context): 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) // 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. val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360 // Return the corresponding FirebaseVisionImageMetadata rotation value. val result: Int when (rotationCompensation) { 0 -> result = FirebaseVisionImageMetadata.ROTATION_0 90 -> result = FirebaseVisionImageMetadata.ROTATION_90 180 -> result = FirebaseVisionImageMetadata.ROTATION_180 270 -> result = FirebaseVisionImageMetadata.ROTATION_270 else -> { result = FirebaseVisionImageMetadata.ROTATION_0 Log.e(TAG, "Bad rotation value: $rotationCompensation") } } return result }
Daha sonra,
media.Image
nesnesini ve döndürme değeriniFirebaseVisionImage.fromMediaImage()
değerine iletin:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Dosya URI'sinden bir
FirebaseVisionImage
nesnesi oluşturmak için uygulama bağlamını ve dosya URI'siniFirebaseVisionImage.fromFilePath()
öğesine iletin. Bu, kullanıcıdan galeri uygulamasından resim seçmesini istemek içinACTION_GET_CONTENT
niyeti kullandığınızda yararlı olur.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin+KTX
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- Bir
ByteBuffer
veya bayt dizisindenFirebaseVisionImage
nesnesi oluşturmak için önceliklemedia.Image
girişi için yukarıda açıklandığı gibi görüntü döndürmesini hesaplayın.Ardından resmin yüksekliği, genişliği, renk kodlama biçimini ve dönüşünü içeren bir
FirebaseVisionImageMetadata
nesnesi oluşturun:Java
FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build();
Kotlin+KTX
val metadata = FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build()
FirebaseVisionImage
nesnesi oluşturmak için arabelleği veya diziyi ve meta veri nesnesini kullanın:Java
FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
Kotlin+KTX
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
Bitmap
nesnesindenFirebaseVisionImage
nesnesi oluşturmak için:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
nesnesinin temsil ettiği resim, ek döndürme gerekmeden dik olmalıdır.
-
FirebaseVisionBarcodeDetector
öğesinin bir örneğini alın:Java
FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance() .getVisionBarcodeDetector(); // Or, to specify the formats to recognize: // FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance() // .getVisionBarcodeDetector(options);
Kotlin+KTX
val detector = FirebaseVision.getInstance() .visionBarcodeDetector // Or, to specify the formats to recognize: // val detector = FirebaseVision.getInstance() // .getVisionBarcodeDetector(options)
Son olarak, resmi
detectInImage
yöntemine iletin:Java
Task<List<FirebaseVisionBarcode>> result = detector.detectInImage(image) .addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionBarcode>>() { @Override public void onSuccess(List<FirebaseVisionBarcode> barcodes) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Kotlin+KTX
val result = detector.detectInImage(image) .addOnSuccessListener { barcodes -> // Task completed successfully // ... } .addOnFailureListener { // Task failed with an exception // ... }
3. Barkodlardan bilgi al
Barkod tanıma işlemi başarılı olursaFirebaseVisionBarcode
nesnelerinden oluşan bir liste başarılı dinleyiciye iletilir. Her FirebaseVisionBarcode
nesnesi, resimde algılanan bir barkodu temsil eder. Her bir barkod için sınırlayıcı koordinatlarının yanı sıra barkodla kodlanan ham verileri de giriş görüntüsünden alabilirsiniz. Barkod algılayıcısı, barkodla kodlanan veri türünü belirleyebilirse ayrıştırılmış veri içeren bir nesne de alabilirsiniz.
Örnek:
Java
for (FirebaseVisionBarcode 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 FirebaseVisionBarcode.TYPE_WIFI: String ssid = barcode.getWifi().getSsid(); String password = barcode.getWifi().getPassword(); int type = barcode.getWifi().getEncryptionType(); break; case FirebaseVisionBarcode.TYPE_URL: String title = barcode.getUrl().getTitle(); String url = barcode.getUrl().getUrl(); break; } }
Kotlin+KTX
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) { FirebaseVisionBarcode.TYPE_WIFI -> { val ssid = barcode.wifi!!.ssid val password = barcode.wifi!!.password val type = barcode.wifi!!.encryptionType } FirebaseVisionBarcode.TYPE_URL -> { val title = barcode.url!!.title val url = barcode.url!!.url } } }
Gerçek zamanlı performansı iyileştirmeye yönelik ipuçları
Barkodları gerçek zamanlı bir uygulamada taramak istiyorsanız en iyi kare hızlarına ulaşmak için aşağıdaki yönergeleri 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 megapikselden büyük) görüntüler üretir. Bu durum, doğruluktan hiçbir şekilde ödün vermeden çok düşük gecikmeye neden olur. Bunun yerine, kameradan yalnızca barkod algılama için gerekli olan boyutu isteyin (genellikle 2 megapikselden fazla değildir).
Tarama hızı önemliyse görüntü yakalama çözünürlüğünü daha da düşürebilirsiniz. Ancak yukarıda belirtilen minimum barkod boyutu gereksinimlerini göz önünde bulundurun.
- Algılayıcıya yapılan çağrıları hızlandırın. Algılayıcı çalışırken yeni bir video karesi kullanılabilir hale gelirse kareyi bırakın.
- Algılayıcının çıkışını giriş görüntüsüne grafik yerleştirmek için kullanıyorsanız önce ML Kit'ten sonucu alın, ardından görüntüyü oluşturun ve tek bir adımda bindirme yapın. Böylece, her giriş karesi için görüntü yüzeyinde yalnızca bir kez oluşturma yaparsınız.
-
Camera2 API'yi kullanıyorsanız görüntüleri
ImageFormat.YUV_420_888
biçiminde çekin.Eski Kamera API'sini kullanıyorsanız görüntüleri
ImageFormat.NV21
biçiminde çekin.