Google 致力于为黑人社区推动种族平等。查看具体举措
此页面由 Cloud Translation API 翻译。
Switch to English

Scanner les codes-barres avec ML Kit sur Android

Vous pouvez utiliser ML Kit pour reconnaître et décoder les codes-barres.

Avant de commencer

  1. Si vous ne l'avez pas déjà fait, ajoutez Firebase à votre projet Android .
  2. Dans votre fichier build.gradle niveau du build.gradle , assurez-vous d'inclure le référentiel Maven de Google dans vos sections buildscript et allprojects .
  3. Ajoutez les dépendances pour les bibliothèques ML Kit Android à votre fichier Gradle de module (au niveau de l'application) (généralement 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'
    }
    

Instructions relatives à l'image d'entrée

  • Pour que ML Kit lise avec précision les codes-barres, les images d'entrée doivent contenir des codes-barres représentés par des données de pixels suffisantes.

    Les exigences spécifiques en matière de données de pixels dépendent à la fois du type de code-barres et de la quantité de données qui y sont codées (puisque la plupart des codes-barres prennent en charge une charge utile de longueur variable). En général, la plus petite unité significative du code à barres doit avoir au moins 2 pixels de large (et pour les codes à 2 dimensions, 2 pixels de haut).

    Par exemple, les codes-barres EAN-13 sont constitués de barres et d'espaces d'une largeur de 1, 2, 3 ou 4 unités, de sorte qu'une image de code-barres EAN-13 comporte idéalement des barres et des espaces d'au moins 2, 4, 6 et 8 pixels de large. Étant donné qu'un code-barres EAN-13 a une largeur totale de 95 unités, le code-barres doit avoir au moins 190 pixels de large.

    Les formats plus denses, tels que PDF417, ont besoin de plus grandes dimensions de pixels pour que ML Kit puisse les lire de manière fiable. Par exemple, un code PDF417 peut contenir jusqu'à 34 "mots" de 17 unités de large sur une seule ligne, ce qui serait idéalement au moins 1156 pixels de large.

  • Une mauvaise mise au point de l'image peut nuire à la précision de numérisation. Si vous n'obtenez pas de résultats acceptables, essayez de demander à l'utilisateur de recapturer l'image.

  • Pour les applications typiques, il est recommandé de fournir une image de résolution plus élevée (telle que 1280x720 ou 1920x1080), ce qui rend les codes-barres détectables à une plus grande distance de la caméra.

    Cependant, dans les applications où la latence est critique, vous pouvez améliorer les performances en capturant des images à une résolution inférieure, mais en exigeant que le code-barres constitue la majorité de l'image d'entrée. Consultez également les conseils pour améliorer les performances en temps réel .

1. Configurer le détecteur de code-barres

Si vous connaissez les formats de codes à barres que vous prévoyez de lire, vous pouvez améliorer la vitesse du détecteur de codes à barres en le configurant pour ne détecter que ces formats.

Par exemple, pour détecter uniquement le code aztèque et les codes QR, FirebaseVisionBarcodeDetectorOptions un objet FirebaseVisionBarcodeDetectorOptions comme dans l'exemple suivant:

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

Les formats suivants sont pris en charge:

  • 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 )
  • Code QR ( FORMAT_QR_CODE )
  • PDF417 ( FORMAT_PDF417 )
  • Aztèque ( FORMAT_AZTEC )
  • Matrice de données ( FORMAT_DATA_MATRIX )

2. Exécutez le détecteur de code-barres

Pour reconnaître les codes-barres dans une image, créez un objet FirebaseVisionImage partir d'un Bitmap , d'un media.Image , d'un ByteBuffer , d'un tableau d'octets ou d'un fichier sur l'appareil. Ensuite, transmettez l'objet FirebaseVisionImage à la méthode detectInImage FirebaseVisionBarcodeDetector .

  1. Créez un objet FirebaseVisionImage partir de votre image.

    • Pour créer un objet FirebaseVisionImage partir d'un objet media.Image , par exemple lors de la capture d'une image à partir de la caméra d'un périphérique, transmettez l'objet media.Image et la rotation de l'image à FirebaseVisionImage.fromMediaImage() .

      Si vous utilisez la bibliothèque CameraX , les classes OnImageCapturedListener et ImageAnalysis.Analyzer calculent la valeur de rotation pour vous, il vous suffit donc de convertir la rotation en l'une des constantes ROTATION_ de ML Kit avant d'appeler 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
                  // ...
              }
          }
      }
      

      Si vous n'utilisez pas de bibliothèque de caméras qui vous donne la rotation de l'image, vous pouvez la calculer à partir de la rotation de l'appareil et de l'orientation du capteur de la caméra dans l'appareil:

      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
      }

      Ensuite, transmettez l'objet media.Image et la valeur de rotation à FirebaseVisionImage.fromMediaImage() :

      Java

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

      Kotlin + KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Pour créer un objet FirebaseVisionImage partir d'un URI de fichier, transmettez le contexte de l'application et l'URI du fichier à FirebaseVisionImage.fromFilePath() . Cela est utile lorsque vous utilisez une intention ACTION_GET_CONTENT pour inviter l'utilisateur à sélectionner une image dans son application de galerie.

      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()
      }
    • Pour créer un objet FirebaseVisionImage partir d'un ByteBuffer ou d'un tableau d'octets, commencez par calculer la rotation de l'image comme décrit ci-dessus pour l'entrée media.Image .

      Ensuite, créez un objet FirebaseVisionImageMetadata qui contient la hauteur, la largeur, le format de codage des couleurs et la rotation de l'image:

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

      Utilisez le tampon ou le tableau et l'objet de métadonnées pour créer un objet 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)
    • Pour créer un objet FirebaseVisionImage partir d'un objet Bitmap :

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin + KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      L'image représentée par l'objet Bitmap doit être verticale, sans rotation supplémentaire requise.

  2. Obtenez une instance de 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. Enfin, transmettez l'image à la méthode 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. Obtenir des informations à partir des codes-barres

Si l'opération de reconnaissance de code-barres réussit, une liste d'objets FirebaseVisionBarcode sera transmise à l'écouteur de réussite. Chaque objet FirebaseVisionBarcode représente un code-barres qui a été détecté dans l'image. Pour chaque code-barres, vous pouvez obtenir ses coordonnées de délimitation dans l'image d'entrée, ainsi que les données brutes encodées par le code-barres. De plus, si le détecteur de code-barres a pu déterminer le type de données encodées par le code-barres, vous pouvez obtenir un objet contenant des données analysées.

Par exemple:

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

Conseils pour améliorer les performances en temps réel

Si vous souhaitez numériser des codes-barres dans une application en temps réel, suivez ces instructions pour obtenir les meilleurs cadences:

  • Ne capturez pas l'entrée à la résolution native de la caméra. Sur certains appareils, la capture d'entrée à la résolution native produit des images extrêmement grandes (plus de 10 mégapixels), ce qui entraîne une latence très faible sans aucun avantage pour la précision. Au lieu de cela, ne demandez à l'appareil photo que la taille requise pour la détection des codes-barres: généralement pas plus de 2 mégapixels.

    Si la vitesse de numérisation est importante, vous pouvez réduire davantage la résolution de capture d'image. Cependant, gardez à l'esprit les exigences minimales de taille de code à barres décrites ci-dessus.

  • Throttle appelle le détecteur. Si une nouvelle image vidéo devient disponible pendant que le détecteur fonctionne, supprimez l'image.
  • Si vous utilisez la sortie du détecteur pour superposer des graphiques sur l'image d'entrée, obtenez d'abord le résultat de ML Kit, puis effectuez le rendu de l'image et de la superposition en une seule étape. Ce faisant, vous effectuez un rendu sur la surface d'affichage une seule fois pour chaque image d'entrée.
  • Si vous utilisez l'API Camera2, capturez des images au format ImageFormat.YUV_420_888 .

    Si vous utilisez l'ancienne API Camera, capturez des images au format ImageFormat.NV21 .