Сканируйте штрих-коды с помощью ML Kit на iOS

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

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

  1. Если вы еще не добавили Firebase в свое приложение, сделайте это, выполнив действия, описанные в руководстве по началу работы .
  2. Включите библиотеки ML Kit в свой подфайл:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    После установки или обновления модулей вашего проекта обязательно откройте проект Xcode, используя его .xcworkspace .
  3. Импортируйте Firebase в свое приложение:

    Быстрый

    import Firebase

    Цель-C

    @import Firebase;

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

  • Чтобы 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. Настройте детектор штрих-кода

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

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

Быстрый

let format = VisionBarcodeFormat.all
let barcodeOptions = VisionBarcodeDetectorOptions(formats: format)

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

  • Код128
  • Код39
  • Код93
  • КодаБар
  • EAN13
  • EAN8
  • МФТ
  • УПКА
  • УПЦЭ
  • QR код
  • PDF417
  • ацтекский
  • DataMatrix

Цель-C

FIRVisionBarcodeDetectorOptions *options =
    [[FIRVisionBarcodeDetectorOptions alloc]
     initWithFormats: FIRVisionBarcodeFormatQRCode | FIRVisionBarcodeFormatAztec];

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

  • Код 128 ( FIRVisionBarcodeFormatCode128 )
  • Код 39 ( FIRVisionBarcodeFormatCode39 )
  • Код 93 ( FIRVisionBarcodeFormatCode93 )
  • Кодабар ( FIRVisionBarcodeFormatCodaBar )
  • EAN-13 ( FIRVisionBarcodeFormatEAN13 )
  • EAN-8 ( FIRVisionBarcodeFormatEAN8 )
  • ITF ( FIRVisionBarcodeFormatITF )
  • UPC-A ( FIRVisionBarcodeFormatUPCA )
  • UPC-E ( FIRVisionBarcodeFormatUPCE )
  • QR-код ( FIRVisionBarcodeFormatQRCode )
  • PDF417 ( FIRVisionBarcodeFormatPDF417 )
  • Ацтекский ( FIRVisionBarcodeFormatAztec )
  • Матрица данных ( FIRVisionBarcodeFormatDataMatrix )

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

Чтобы сканировать штрих-коды в изображении, передайте изображение как UIImage или CMSampleBufferRef в метод detect(in:) VisionBarcodeDetector :

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

    Быстрый

    lazy var vision = Vision.vision()
    
    let barcodeDetector = vision.barcodeDetector(options: barcodeOptions)
    

    Цель-C

    FIRVision *vision = [FIRVision vision];
    FIRVisionBarcodeDetector *barcodeDetector = [vision barcodeDetector];
    // Or, to change the default settings:
    // FIRVisionBarcodeDetector *barcodeDetector =
    //     [vision barcodeDetectorWithOptions:options];
    
  2. Создайте объект VisionImage , используя UIImage или CMSampleBufferRef .

    Чтобы использовать UIImage :

    1. При необходимости поверните изображение так, чтобы его свойство imageOrientation имело значение .up .
    2. Создайте объект VisionImage , используя правильно повернутый UIImage . Не указывайте метаданные вращения — необходимо использовать значение по умолчанию .topLeft .

      Быстрый

      let image = VisionImage(image: uiImage)

      Цель-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    Чтобы использовать CMSampleBufferRef :

    1. Создайте объект VisionImageMetadata , который задает ориентацию данных изображения, содержащихся в буфере CMSampleBufferRef .

      Чтобы получить ориентацию изображения:

      Быстрый

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Цель-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      Затем создайте объект метаданных:

      Быстрый

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Цель-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. Создайте объект VisionImage , используя объект CMSampleBufferRef и метаданные вращения:

      Быстрый

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Цель-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Затем передайте изображение методу detect(in:) :

    Быстрый

    barcodeDetector.detect(in: visionImage) { features, error in
      guard error == nil, let features = features, !features.isEmpty else {
        // ...
        return
      }
    
      // ...
    }
    

    Цель-C

    [barcodeDetector detectInImage:image
                        completion:^(NSArray<FIRVisionBarcode *> *barcodes,
                                     NSError *error) {
      if (error != nil) {
        return;
      } else if (barcodes != nil) {
        // Recognized barcodes
        // ...
      }
    }];
    

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

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

Например:

Быстрый

for barcode in barcodes {
  let corners = barcode.cornerPoints

  let displayValue = barcode.displayValue
  let rawValue = barcode.rawValue

  let valueType = barcode.valueType
  switch valueType {
  case .wiFi:
    let ssid = barcode.wifi!.ssid
    let password = barcode.wifi!.password
    let encryptionType = barcode.wifi!.type
  case .URL:
    let title = barcode.url!.title
    let url = barcode.url!.url
  default:
    // See API reference for all supported value types
  }
}

Цель-C

 for (FIRVisionBarcode *barcode in barcodes) {
   NSArray *corners = barcode.cornerPoints;

   NSString *displayValue = barcode.displayValue;
   NSString *rawValue = barcode.rawValue;

   FIRVisionBarcodeValueType valueType = barcode.valueType;
   switch (valueType) {
     case FIRVisionBarcodeValueTypeWiFi:
       // ssid = barcode.wifi.ssid;
       // password = barcode.wifi.password;
       // encryptionType = barcode.wifi.type;
       break;
     case FIRVisionBarcodeValueTypeURL:
       // url = barcode.URL.url;
       // title = barcode.URL.title;
       break;
     // ...
     default:
       break;
   }
 }

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

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

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

    Однако именованные предустановки сеанса захвата — AVCaptureSessionPresetDefault , AVCaptureSessionPresetLow , AVCaptureSessionPresetMedium и т. д.) — не рекомендуются, поскольку они могут соответствовать неподходящим разрешениям на некоторых устройствах. Вместо этого используйте определенные пресеты, такие как AVCaptureSessionPreset1280x720 .

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

  • Дроссель вызывает детектор. Если новый видеокадр становится доступным во время работы детектора, удалите этот кадр.
  • Если вы используете выходные данные детектора для наложения графики на входное изображение, сначала получите результат из ML Kit, затем визуализируйте изображение и наложите его за один шаг. При этом вы выполняете рендеринг на поверхность дисплея только один раз для каждого входного кадра. Пример см. в классах PreviewOverlayView и FIRDetectionOverlayView в демонстрационном примере приложения.