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

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

Попробуйте это

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

  1. Включите в свой подфайл следующие модули ML Kit:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. После установки или обновления модулей вашего проекта откройте проект Xcode, используя его .xcworkspace . ML Kit поддерживается в Xcode версии 12.4 или новее.

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

  • Чтобы 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-коды, создайте объект BarcodeScannerOptions , как показано в следующем примере:

Быстрый

let format = .all
let barcodeOptions = BarcodeScannerOptions(formats: format)
  

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

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

Цель-C

MLKBarcodeScannerOptions *options =
  [[MLKBarcodeScannerOptions alloc]
   initWithFormats: MLKBarcodeFormatQRCode | MLKBarcodeFormatAztec];

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

  • Код-128 ( MLKBarcodeFormatCode128 )
  • Код-39 ( MLKBarcodeFormatCode39 )
  • Код-93 ( MLKBarcodeFormatCode93 )
  • Кодабар ( MLKBarcodeFormatCodaBar )
  • Матрица данных ( MLKBarcodeFormatDataMatrix )
  • EAN-13 ( MLKBarcodeFormatEAN13 )
  • EAN-8 ( MLKBarcodeFormatEAN8 )
  • ITF ( MLKBarcodeFormatITF )
  • QR-код ( MLKBarcodeFormatQRCode )
  • UPC-A ( MLKBarcodeFormatUPCA )
  • UPC-E ( MLKBarcodeFormatUPCE )
  • PDF-417 ( MLKBarcodeFormatPDF417 )
  • Ацтекский код ( MLKBarcodeFormatAztec )

2. Подготовьте входное изображение

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

Создайте объект VisionImage , используя UIImage или CMSampleBuffer .

Если вы используете UIImage , выполните следующие действия:

  • Создайте объект VisionImage с помощью UIImage . Обязательно укажите правильную .orientation .

    Быстрый

    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation

    Цель-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

Если вы используете CMSampleBuffer , выполните следующие действия:

  • Укажите ориентацию данных изображения, содержащихся в CMSampleBuffer .

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

    Быстрый

    func imageOrientation(
      deviceOrientation: UIDeviceOrientation,
      cameraPosition: AVCaptureDevice.Position
    ) -> UIImage.Orientation {
      switch deviceOrientation {
      case .portrait:
        return cameraPosition == .front ? .leftMirrored : .right
      case .landscapeLeft:
        return cameraPosition == .front ? .downMirrored : .up
      case .portraitUpsideDown:
        return cameraPosition == .front ? .rightMirrored : .left
      case .landscapeRight:
        return cameraPosition == .front ? .upMirrored : .down
      case .faceDown, .faceUp, .unknown:
        return .up
      }
    }
          

    Цель-C

    - (UIImageOrientation)
      imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                             cameraPosition:(AVCaptureDevicePosition)cameraPosition {
      switch (deviceOrientation) {
        case UIDeviceOrientationPortrait:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored
                                                                : UIImageOrientationRight;
    
        case UIDeviceOrientationLandscapeLeft:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored
                                                                : UIImageOrientationUp;
        case UIDeviceOrientationPortraitUpsideDown:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored
                                                                : UIImageOrientationLeft;
        case UIDeviceOrientationLandscapeRight:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored
                                                                : UIImageOrientationDown;
        case UIDeviceOrientationUnknown:
        case UIDeviceOrientationFaceUp:
        case UIDeviceOrientationFaceDown:
          return UIImageOrientationUp;
      }
    }
          
  • Создайте объект VisionImage , используя объект CMSampleBuffer и ориентацию:

    Быстрый

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    Цель-C

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

3. Получите экземпляр BarcodeScanner.

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

Быстрый

let barcodeScanner = BarcodeScanner.barcodeScanner()
// Or, to change the default settings:
// let barcodeScanner = BarcodeScanner.barcodeScanner(options: barcodeOptions)

Цель-C

MLKBarcodeScanner *barcodeScanner = [MLKBarcodeScanner barcodeScanner];
// Or, to change the default settings:
// MLKBarcodeScanner *barcodeScanner =
//     [MLKBarcodeScanner barcodeScannerWithOptions:options];

4. Обработка изображения

Затем передайте изображение в process() :

Быстрый

barcodeScanner.process(visionImage) { features, error in
  guard error == nil, let features = features, !features.isEmpty else {
    // Error handling
    return
  }
  // Recognized barcodes
}

Цель-C

[barcodeScanner processImage:image
                  completion:^(NSArray<MLKBarcode *> *_Nullable barcodes,
                               NSError *_Nullable error) {
  if (error != nil) {
    // Error handling
    return;
  }
  if (barcodes.count > 0) {
    // Recognized barcodes
  }
}];

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

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

Например:

Быстрый

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 (MLKBarcode *barcode in barcodes) {
   NSArray *corners = barcode.cornerPoints;

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

   MLKBarcodeValueType valueType = barcode.valueType;
   switch (valueType) {
     case MLKBarcodeValueTypeWiFi:
       ssid = barcode.wifi.ssid;
       password = barcode.wifi.password;
       encryptionType = barcode.wifi.type;
       break;
     case MLKBarcodeValueTypeURL:
       url = barcode.URL.url;
       title = barcode.URL.title;
       break;
     // ...
     default:
       break;
   }
 }

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

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

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

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

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

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

    Цифра контрольной суммы не поддерживается для ITF и CODE-39.

  • Для обработки видеокадров используйте синхронный API results(in:) детектора. Вызовите этот метод из captureOutput(_, didOutput:from:) AVCaptureVideoDataOutputSampleBufferDelegate , чтобы синхронно получить результаты из данного видеокадра. Оставьте AVCaptureVideoDataOutput значение alwaysDiscardsLateVideoFrames как true , чтобы ограничить вызовы детектора. Если во время работы детектора появится новый видеокадр, он будет удален.
  • Если вы используете выходные данные детектора для наложения графики на входное изображение, сначала получите результат из ML Kit, затем визуализируйте изображение и наложите его за один шаг. При этом вы выполняете рендеринг на поверхность дисплея только один раз для каждого обработанного входного кадра. Пример см. в updatePreviewOverlayViewWithLastFrame в образце быстрого запуска ML Kit.