Scannen Sie Barcodes mit ML Kit auf Android

Mit dem ML Kit können Sie Barcodes erkennen und dekodieren.

Bevor Sie beginnen

  1. Falls noch nicht geschehen, fügen Sie Firebase zu Ihrem Android-Projekt hinzu .
  2. Fügen Sie die Abhängigkeiten für die ML Kit-Android-Bibliotheken zu Ihrer Modul-Gradle-Datei (auf App-Ebene) hinzu (normalerweise app/build.gradle ):
    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'
    }
    

Geben Sie Bildrichtlinien ein

  • Damit ML Kit Barcodes genau lesen kann, müssen Eingabebilder Barcodes enthalten, die durch ausreichend Pixeldaten dargestellt werden.

    Die spezifischen Pixeldatenanforderungen hängen sowohl von der Art des Barcodes als auch von der darin codierten Datenmenge ab (da die meisten Barcodes eine Nutzlast variabler Länge unterstützen). Im Allgemeinen sollte die kleinste aussagekräftige Einheit des Barcodes mindestens 2 Pixel breit (und bei 2-dimensionalen Codes 2 Pixel hoch) sein.

    EAN-13-Barcodes bestehen beispielsweise aus Strichen und Zwischenräumen mit einer Breite von 1, 2, 3 oder 4 Einheiten. Daher weist ein EAN-13-Barcodebild idealerweise Striche und Zwischenräume mit einer Breite von mindestens 2, 4, 6 und auf 8 Pixel breit. Da ein EAN-13-Barcode insgesamt 95 Einheiten breit ist, sollte der Barcode mindestens 190 Pixel breit sein.

    Dichtere Formate wie PDF417 benötigen größere Pixelabmessungen, damit ML Kit sie zuverlässig lesen kann. Beispielsweise kann ein PDF417-Code bis zu 34 „Wörter“ mit einer Breite von 17 Einheiten in einer einzelnen Zeile enthalten, die idealerweise mindestens 1156 Pixel breit wäre.

  • Eine schlechte Bildschärfe kann die Scangenauigkeit beeinträchtigen. Wenn Sie keine akzeptablen Ergebnisse erhalten, bitten Sie den Benutzer, das Bild erneut aufzunehmen.

  • Für typische Anwendungen wird empfohlen, ein Bild mit höherer Auflösung (z. B. 1280 x 720 oder 1920 x 1080) bereitzustellen, damit Barcodes aus größerer Entfernung von der Kamera erkennbar sind.

    Bei Anwendungen, bei denen die Latenz entscheidend ist, können Sie die Leistung jedoch verbessern, indem Sie Bilder mit einer niedrigeren Auflösung erfassen, aber verlangen, dass der Barcode den Großteil des Eingabebilds ausmacht. Siehe auch Tipps zur Verbesserung der Echtzeitleistung .

1. Konfigurieren Sie den Barcode-Detektor

Wenn Sie wissen, welche Barcode-Formate Sie voraussichtlich lesen werden, können Sie die Geschwindigkeit des Barcode-Detektors verbessern, indem Sie ihn so konfigurieren, dass er nur diese Formate erkennt.

Um beispielsweise nur Aztec-Code und QR-Codes zu erkennen, erstellen Sie ein FirebaseVisionBarcodeDetectorOptions Objekt wie im folgenden Beispiel:

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

Folgende Formate werden unterstützt:

  • Code 128 ( FORMAT_CODE_128 )
  • Code 39 ( FORMAT_CODE_39 )
  • Code 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-Code ( FORMAT_QR_CODE )
  • PDF417 ( FORMAT_PDF417 )
  • Aztekisch ( FORMAT_AZTEC )
  • Datenmatrix ( FORMAT_DATA_MATRIX )

2. Starten Sie den Barcode-Detektor

Um Barcodes in einem Bild zu erkennen, erstellen Sie ein FirebaseVisionImage Objekt entweder aus einem Bitmap , media.Image , ByteBuffer , einem Byte-Array oder einer Datei auf dem Gerät. Übergeben Sie dann das FirebaseVisionImage Objekt an die Methode detectInImage von FirebaseVisionBarcodeDetector .

  1. Erstellen Sie aus Ihrem Bild ein FirebaseVisionImage Objekt.

    • Um ein FirebaseVisionImage Objekt aus einem media.Image Objekt zu erstellen, beispielsweise beim Aufnehmen eines Bildes von der Kamera eines Geräts, übergeben Sie das media.Image Objekt und die Drehung des Bildes an FirebaseVisionImage.fromMediaImage() .

      Wenn Sie die CameraX- Bibliothek verwenden, berechnen die Klassen OnImageCapturedListener und ImageAnalysis.Analyzer den Rotationswert für Sie, Sie müssen also nur die Rotation in eine der ROTATION_ Konstanten von ML Kit konvertieren, bevor Sie FirebaseVisionImage.fromMediaImage() aufrufen:

      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
                  // ...
              }
          }
      }
      

      Wenn Sie keine Kamerabibliothek verwenden, die Ihnen die Drehung des Bildes liefert, können Sie diese aus der Drehung des Geräts und der Ausrichtung des Kamerasensors im Gerät berechnen:

      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
      }

      Übergeben Sie dann das media.Image Objekt und den Rotationswert an FirebaseVisionImage.fromMediaImage() :

      Java

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

      Kotlin+KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Um ein FirebaseVisionImage Objekt aus einem Datei-URI zu erstellen, übergeben Sie den App-Kontext und den Datei-URI an FirebaseVisionImage.fromFilePath() . Dies ist nützlich, wenn Sie eine ACTION_GET_CONTENT Absicht verwenden, um den Benutzer aufzufordern, ein Bild aus seiner Galerie-App auszuwählen.

      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()
      }
    • Um ein FirebaseVisionImage Objekt aus einem ByteBuffer oder einem Byte-Array zu erstellen, berechnen Sie zunächst die Bildrotation wie oben für media.Image Eingabe beschrieben.

      Erstellen Sie dann ein FirebaseVisionImageMetadata -Objekt, das die Höhe, Breite, das Farbkodierungsformat und die Drehung des Bildes enthält:

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

      Verwenden Sie den Puffer oder das Array und das Metadatenobjekt, um ein FirebaseVisionImage Objekt zu erstellen:

      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)
    • So erstellen Sie ein FirebaseVisionImage Objekt aus einem Bitmap Objekt:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      Das durch das Bitmap Objekt dargestellte Bild muss aufrecht stehen, ohne dass eine zusätzliche Drehung erforderlich ist.

  2. Holen Sie sich eine Instanz von FirebaseVisionBarcodeDetector :

    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)
  3. Übergeben Sie abschließend das Bild an die Methode detectInImage :

    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. Erhalten Sie Informationen aus Barcodes

Wenn der Barcode-Erkennungsvorgang erfolgreich ist, wird eine Liste von FirebaseVisionBarcode Objekten an den Erfolgs-Listener übergeben. Jedes FirebaseVisionBarcode Objekt stellt einen Barcode dar, der im Bild erkannt wurde. Für jeden Barcode können Sie seine Grenzkoordinaten im Eingabebild sowie die vom Barcode codierten Rohdaten abrufen. Wenn der Barcode-Detektor außerdem die Art der durch den Barcode codierten Daten ermitteln konnte, können Sie ein Objekt erhalten, das analysierte Daten enthält.

Zum Beispiel:

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

Tipps zur Verbesserung der Echtzeitleistung

Wenn Sie Barcodes in einer Echtzeitanwendung scannen möchten, befolgen Sie diese Richtlinien, um die besten Bildraten zu erzielen:

  • Erfassen Sie Eingaben nicht mit der nativen Auflösung der Kamera. Auf einigen Geräten führt die Erfassung von Eingaben mit der nativen Auflösung zu extrem großen Bildern (10+ Megapixel), was zu einer sehr geringen Latenz ohne Vorteile für die Genauigkeit führt. Fordern Sie stattdessen von der Kamera nur die Größe an, die für die Barcode-Erkennung erforderlich ist: in der Regel nicht mehr als 2 Megapixel.

    Wenn die Scangeschwindigkeit wichtig ist, können Sie die Auflösung der Bildaufnahme weiter verringern. Beachten Sie jedoch die oben genannten Mindestanforderungen an die Barcodegröße.

  • Gasrufe an den Detektor. Wenn ein neues Videobild verfügbar wird, während der Detektor läuft, löschen Sie das Bild.
  • Wenn Sie die Ausgabe des Detektors verwenden, um Grafiken auf dem Eingabebild zu überlagern, rufen Sie zunächst das Ergebnis vom ML Kit ab und rendern Sie dann das Bild und überlagern Sie es in einem einzigen Schritt. Auf diese Weise rendern Sie für jeden Eingaberahmen nur einmal auf der Anzeigeoberfläche.
  • Wenn Sie die Camera2-API verwenden, erfassen Sie Bilder im ImageFormat.YUV_420_888 Format.

    Wenn Sie die ältere Kamera-API verwenden, erfassen Sie Bilder im ImageFormat.NV21 Format.