Google is committed to advancing racial equity for Black communities. See how.
Эта страница была переведа с помощью Cloud Translation API.
Switch to English

Сканирование штрих-кодов с помощью ML Kit на Android

jinja

Вы можете использовать ML Kit для распознавания и декодирования штрих-кодов.

Прежде чем вы начнете

  1. Если вы еще этого не сделали, добавьте Firebase в свой проект Android .
  2. В своем файле build.gradle уровня проекта обязательно build.gradle репозиторий Google Maven в buildscript и allprojects .
  3. Добавьте зависимости для библиотек ML Kit Android в ваш файл Gradle модуля (на уровне приложения) (обычно 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'
    }
    

Рекомендации по вводу изображений

  • Чтобы ML Kit точно считывал штрих-коды, входные изображения должны содержать штрих-коды, представленные достаточным количеством пикселей.

    Требования к конкретным пиксельным данным зависят как от типа штрих-кода, так и от объема данных, которые в нем кодируются (поскольку большинство штрих-кодов поддерживают полезную нагрузку переменной длины). Как правило, наименьшая значимая единица штрих-кода должна иметь ширину не менее 2 пикселей (а для двумерных кодов - 2 пикселя).

    Например, штрих-коды EAN-13 состоят из полос и пробелов шириной в 1, 2, 3 или 4 единицы, поэтому в идеале изображение штрих-кода EAN-13 должно иметь полосы и пробелы не менее 2, 4, 6 и 8 пикселей в ширину. Поскольку штрих-код EAN-13 имеет общую ширину 95 единиц, он должен иметь ширину не менее 190 пикселей.

    Более плотным форматам, таким как PDF417, требуются большие размеры в пикселях, чтобы ML Kit мог их надежно читать. Например, код PDF417 может содержать до 34 «слов» шириной 17 единиц в одной строке, которая в идеале должна иметь ширину не менее 1156 пикселей.

  • Плохая фокусировка изображения может снизить точность сканирования. Если вы не получаете приемлемых результатов, попробуйте попросить пользователя восстановить изображение.

  • Для типичных приложений рекомендуется предоставлять изображение с более высоким разрешением (например, 1280x720 или 1920x1080), что позволяет обнаруживать штрих-коды на большем расстоянии от камеры.

    Однако в приложениях, где задержка имеет решающее значение, вы можете повысить производительность, снимая изображения с более низким разрешением, но требуя, чтобы штрих-код составлял большую часть входного изображения. Также см. Советы по улучшению производительности в реальном времени .

1. Настройте детектор штрих-кода

Если вы знаете, какие форматы штрих-кодов вы ожидаете прочитать, вы можете повысить скорость детектора штрих-кодов, настроив его только для обнаружения этих форматов.

Например, чтобы обнаружить только код Aztec и QR-коды, создайте объект FirebaseVisionBarcodeDetectorOptions как в следующем примере:

Ява

FirebaseVisionBarcodeDetectorOptions options =
        new FirebaseVisionBarcodeDetectorOptions.Builder()
        .setBarcodeFormats(
                FirebaseVisionBarcode.FORMAT_QR_CODE,
                FirebaseVisionBarcode.FORMAT_AZTEC)
        .build();

Котлин + KTX

val options = FirebaseVisionBarcodeDetectorOptions.Builder()
        .setBarcodeFormats(
                FirebaseVisionBarcode.FORMAT_QR_CODE,
                FirebaseVisionBarcode.FORMAT_AZTEC)
        .build()

Поддерживаются следующие форматы:

  • Код 128 ( FORMAT_CODE_128 )
  • Код 39 ( FORMAT_CODE_39 )
  • Код 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-код ( FORMAT_QR_CODE )
  • PDF417 ( FORMAT_PDF417 )
  • Ацтек ( FORMAT_AZTEC )
  • Матрица данных ( FORMAT_DATA_MATRIX )

2. Запустите детектор штрих-кода

Чтобы распознать штрих-коды в изображении, создайте объект FirebaseVisionImage из Bitmap , media.Image , ByteBuffer , байтового массива или файла на устройстве. Затем передать FirebaseVisionImage объект в FirebaseVisionBarcodeDetector «s detectInImage метода.

  1. Создайте объект FirebaseVisionImage из вашего изображения.

    • Чтобы создать объект FirebaseVisionImage из объекта media.Image , например, при захвате изображения с камеры устройства, media.Image объект media.Image и поворот изображения в FirebaseVisionImage.fromMediaImage() .

      Если вы используете CameraX библиотеку, OnImageCapturedListener и ImageAnalysis.Analyzer классов вычислить значение вращения для вас, так что вам просто нужно , чтобы преобразовать вращение одного из ML Кита ROTATION_ констант перед вызовом FirebaseVisionImage.fromMediaImage() :

      Ява

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

      Котлин + 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
                  // ...
              }
          }
      }
      

      Если вы не используете библиотеку камеры, которая дает вам поворот изображения, вы можете рассчитать его по повороту устройства и ориентации датчика камеры в устройстве:

      Ява

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

      Котлин + 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
      }

      Затем передайте объект media.Image и значение поворота в FirebaseVisionImage.fromMediaImage() :

      Ява

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

      Котлин + KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Чтобы создать объект FirebaseVisionImage из URI файла, передайте контекст приложения и URI файла в FirebaseVisionImage.fromFilePath() . Это полезно, когда вы используете намерение ACTION_GET_CONTENT чтобы предложить пользователю выбрать изображение из приложения галереи.

      Ява

      FirebaseVisionImage image;
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e.printStackTrace();
      }

      Котлин + KTX

      val image: FirebaseVisionImage
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri)
      } catch (e: IOException) {
          e.printStackTrace()
      }
    • Чтобы создать объект FirebaseVisionImage из ByteBuffer или байтового массива, сначала рассчитайте поворот изображения, как описано выше для ввода media.Image .

      Затем создайте объект FirebaseVisionImageMetadata который содержит высоту изображения, ширину, формат цветовой кодировки и поворот:

      Ява

      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
              .setWidth(480)   // 480x360 is typically sufficient for
              .setHeight(360)  // image recognition
              .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
              .setRotation(rotation)
              .build();

      Котлин + 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 :

      Ява

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);

      Котлин + KTX

      val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
      // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    • Чтобы создать объект FirebaseVisionImage из объекта Bitmap :

      Ява

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Котлин + KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      Изображение, представленное Bitmap объектом, должно быть в вертикальном положении, без дополнительного поворота.

  2. Получите экземпляр FirebaseVisionBarcodeDetector :

    Ява

    FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance()
            .getVisionBarcodeDetector();
    // Or, to specify the formats to recognize:
    // FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance()
    //        .getVisionBarcodeDetector(options);

    Котлин + KTX

    val detector = FirebaseVision.getInstance()
            .visionBarcodeDetector
    // Or, to specify the formats to recognize:
    // val detector = FirebaseVision.getInstance()
    //        .getVisionBarcodeDetector(options)
  3. Наконец, передайте изображение в метод detectInImage :

    Ява

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

    Котлин + KTX

    val result = detector.detectInImage(image)
            .addOnSuccessListener { barcodes ->
                // Task completed successfully
                // ...
            }
            .addOnFailureListener {
                // Task failed with an exception
                // ...
            }

3. Получить информацию из штрих-кодов

Если операция распознавания штрих-кода выполнена успешно, список объектов FirebaseVisionBarcode будет передан слушателю успеха. Каждый объект FirebaseVisionBarcode представляет штрих-код, который был обнаружен в изображении. Для каждого штрих-кода вы можете получить его ограничивающие координаты во входном изображении, а также необработанные данные, закодированные штрих-кодом. Также, если детектор штрих-кода смог определить тип данных, закодированных штрих-кодом, вы можете получить объект, содержащий проанализированные данные.

Например:

Ява

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

Котлин + 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
        }
    }
}

Советы по улучшению производительности в реальном времени

Если вы хотите сканировать штрих-коды в приложении в реальном времени, следуйте этим рекомендациям, чтобы достичь наилучшей частоты кадров:

  • Не фиксируйте ввод с собственным разрешением камеры. На некоторых устройствах при вводе данных с собственным разрешением изображения получаются очень большими (10+ мегапикселей), что приводит к очень низкой задержке без повышения точности. Вместо этого запрашивайте только размер камеры, необходимый для обнаружения штрих-кода: обычно не более 2 мегапикселей.

    Если важна скорость сканирования, вы можете еще больше снизить разрешение захвата изображения. Однако имейте в виду минимальные требования к размеру штрих-кода, изложенные выше.

  • Дроссель зовет к детектору. Если новый видеокадр становится доступным во время работы детектора, отбросьте кадр.
  • Если вы используете выход детектора для наложения графики на входное изображение, сначала получите результат из ML Kit, а затем визуализируйте изображение и наложение за один шаг. Тем самым вы визуализируете на поверхность дисплея только один раз для каждого входного кадра.
  • Если вы используете Camera2 API, ImageFormat.YUV_420_888 изображения в формате ImageFormat.YUV_420_888 .

    Если вы используете более старый Camera API, ImageFormat.NV21 изображения в формате ImageFormat.NV21 .