Barcodes mit ML Kit unter Android scannen

Mit ML Kit können Sie Barcodes erkennen und decodieren.

Hinweis

  1. Falls noch nicht geschehen, Fügen Sie Firebase zu Ihrem Android-Projekt hinzu.
  2. Abhängigkeiten für die ML Kit-Android-Bibliotheken zu Ihrem Modul hinzufügen Gradle-Datei auf App-Ebene (in der Regel 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'
    }

Richtlinien für Eingabebilder

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

    Die spezifischen Pixeldatenanforderungen hängen sowohl vom Typ Barcode und die darin codierte Datenmenge (da die meisten Barcodes eine Nutzlast mit variabler Länge unterstützen). Im Allgemeinen sollte die kleinste sollte die Einheit des Barcodes mindestens 2 Pixel breit sein (und für 2-dimensionale Codes, 2 Pixel hoch).

    EAN-13-Barcodes bestehen beispielsweise aus Balken und Leerräumen, die 1, 2, 3 oder 4 Einheiten breit sind. Ein EAN-13-Barcodebild sollte also idealerweise Balken und Leerräume mit einer Breite von mindestens 2, 4, 6 und 8 Pixeln haben. Da eine EAN-13- Barcode insgesamt 95 Einheiten breit ist, sollte der Barcode mindestens 190 Einheiten haben. Pixel breit.

    Kompaktere Formate wie PDF417 erfordern größere Pixelabmessungen für ML Kit zuverlässig lesen. Beispielsweise kann ein PDF417-Code bis zu 34 Breite „Wörter“ (17 Einheiten) in einer einzelnen Zeile, was im Idealfall mindestens 1156 Pixel breit.

  • Ein schlechter Bildfokus kann die Scangenauigkeit beeinträchtigen. Wenn Sie keine akzeptable Ergebnisse, versuchen Sie, das Bild erneut aufzunehmen.

  • Für typische Anwendungen empfiehlt es sich, eine höhere Bildauflösung (z. B. 1280 x 720 oder 1920 x 1080), bei dem Barcodes auch bei größerer Entfernung zur Kamera erkennbar sein.

    Bei Anwendungen, bei denen die Latenz entscheidend ist, können Sie die Leistung jedoch verbessern, indem Sie Bilder mit niedrigerer Auflösung aufnehmen. Der Barcode muss dabei jedoch den Großteil des Eingabebilds ausmachen. Weitere Informationen finden Sie unter Tipps zur Verbesserung der Echtzeitleistung.

1. Barcode-Erkennung konfigurieren

Wenn Sie wissen, welche Barcodeformate Sie lesen möchten, können Sie die Geschwindigkeit verbessern des Barcode-Detektors konfigurieren, indem Sie ihn so konfigurieren, dass nur diese Formate erkannt werden.

Um beispielsweise nur Aztec-Code und QR-Codes zu erkennen, erstellen Sie FirebaseVisionBarcodeDetectorOptions-Objekts 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()

Die folgenden 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)
  • Azteken (FORMAT_AZTEC)
  • Datenmatrix (FORMAT_DATA_MATRIX)

2. Barcode-Detektor ausführen

Wenn du Barcodes in einem Bild erkennen möchtest, erstelle ein FirebaseVisionImage-Objekt aus einem Bitmap-, media.Image-, ByteBuffer-, Byte-Array oder einer Datei in auf dem Gerät. Übergeben Sie dann das FirebaseVisionImage-Objekt an den Die Methode detectInImage von FirebaseVisionBarcodeDetector.

  1. Erstellen Sie aus Ihrem Bild ein FirebaseVisionImage-Objekt.

    • Wenn Sie ein FirebaseVisionImage-Objekt aus einem media.Image-Objekt erstellen möchten, z. B. wenn Sie ein Bild mit der Kamera eines Geräts aufnehmen, übergeben Sie das media.Image-Objekt und die Drehung des Bilds an FirebaseVisionImage.fromMediaImage().

      Wenn Sie die Methode CameraX-Bibliothek, den OnImageCapturedListener und ImageAnalysis.Analyzer-Klassen berechnen den Rotationswert Sie müssen also nur die Rotation in eine der ML Kit-Modelle ROTATION_-Konstanten vor dem Aufruf FirebaseVisionImage.fromMediaImage():

      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 Rotation des Bildes anzeigt, den Wert aus der Gerätedrehung und der Kameraausrichtung berechnen kann. Sensor im Gerät:

      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 auf 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 App-Kontext und Datei-URI zu FirebaseVisionImage.fromFilePath(). Dies ist nützlich, wenn Sie Verwenden Sie den Intent ACTION_GET_CONTENT, um den Nutzer zur Auswahl aufzufordern ein Bild aus ihrer Galerie-App.

      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, berechnen Sie zuerst das Bild Rotation wie oben für die media.Image-Eingabe beschrieben.

      Erstellen Sie dann ein FirebaseVisionImageMetadata-Objekt. die die Höhe, Breite, Farbcodierung, und Rotation:

      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 Zwischenspeicher oder das Array und das Metadatenobjekt, um einen Objekt FirebaseVisionImage:

      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 Objekt Bitmap dargestellte Bild muss aufrecht und ohne zusätzliche Drehung aufrecht.

  2. Rufen Sie eine FirebaseVisionBarcodeDetector-Instanz ab:

    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 zuletzt 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. Informationen aus Barcodes abrufen

Wenn die Barcodeerkennung erfolgreich ist, wird eine Liste FirebaseVisionBarcode-Objekte werden an den Erfolgs-Listener übergeben. Jedes Das Objekt FirebaseVisionBarcode steht für einen Barcode, der in der Bild. Für jeden Barcode finden Sie die Begrenzungskoordinaten in der Eingabe als auch die durch den Barcode codierten Rohdaten. Wenn der Barcode-Detektor den Typ der vom Barcode codierten Daten ermitteln konnte, können Sie auch ein Objekt mit geparsten Daten abrufen.

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 zum Verbessern der Leistung in Echtzeit

Wenn Sie Barcodes in einer Echtzeitanwendung scannen möchten, gehen Sie so vor: um optimale Framerates zu erzielen:

  • Nehmen Sie Eingaben nicht mit der nativen Auflösung der Kamera auf. Auf einigen Geräten ist das Erfassen von Eingaben mit nativer Auflösung extrem groß (10+ Megapixel), was zu einer sehr geringen Latenz führt, Genauigkeit. Fordern Sie stattdessen nur die erforderliche Größe von der Kamera an. zur Barcodeerkennung: in der Regel nicht mehr als 2 Megapixel.

    Wenn die Scangeschwindigkeit wichtig ist, können Sie die Bildaufnahme weiter verringern Problembehebung. Beachten Sie jedoch die Mindestanforderungen an die Größe von Barcodes. wie oben beschrieben.

  • Drosselung von Aufrufen an den Detektor. Wenn ein neuer Videoframe wenn der Detektor ausgeführt wird, lassen Sie den Frame weg.
  • Wenn Sie die Ausgabe des Detektors verwenden, um Grafiken Eingabebild, rufen Sie zuerst das Ergebnis aus ML Kit ab und rendern Sie das Bild in einem Schritt übereinanderlegen. Dadurch rendern Sie auf der Anzeigeoberfläche für jeden Eingabe-Frame nur einmal.
  • Wenn Sie die Camera2 API verwenden, sollten Sie Bilder im ImageFormat.YUV_420_888-Format aufnehmen.

    Wenn Sie die ältere Camera API verwenden, nehmen Sie Bilder in ImageFormat.NV21-Format.