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

С помощью ML Kit можно распознавать и декодировать штрихкоды.

Попробовать

Подготовка

  1. Добавьте в Podfile следующие контейнеры ML Kit:
    pod 'GoogleMLKit/BarcodeScanning', '8.0.0'
    
  2. После установки или обновления Pods откройте проект Xcode, используя файл .xcworkspace. ML Kit поддерживается в Xcode версии 12.4 и более поздних.

Требования к исходным изображениям

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

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

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

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

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

  • Для типичных приложений рекомендуется использовать изображения с более высоким разрешением, например 1280 x 720 или 1920 x 1080, чтобы штрихкоды можно было сканировать с большего расстояния от камеры.

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

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

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

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

Swift

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

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

  • code128
  • code39
  • code93
  • codaBar
  • dataMatrix
  • EAN 13
  • EAN 8
  • ITF
  • qrCode
  • UPCA
  • UPCE
  • PDF417
  • Aztec

Objective-C

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

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

  • Code-128 (MLKBarcodeFormatCode128)
  • Code-39 (MLKBarcodeFormatCode39)
  • Code-93 (MLKBarcodeFormatCode93)
  • Codabar (MLKBarcodeFormatCodaBar)
  • Data Matrix (MLKBarcodeFormatDataMatrix)
  • EAN-13 (MLKBarcodeFormatEAN13)
  • EAN-8 (MLKBarcodeFormatEAN8)
  • ITF (MLKBarcodeFormatITF)
  • QR-код (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • Код Aztec (MLKBarcodeFormatAztec)

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

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

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

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

  • Создайте объект VisionImage с UIImage. Убедитесь, что вы указали правильное значение параметра .orientation.

    Swift

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

    Objective-C

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

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

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

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

    Swift

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

    Objective-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 и ориентацию:

    Swift

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

    Objective-C

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

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

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

Swift

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

Objective-C

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

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

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

Swift

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

Objective-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 представляет собой штрихкод, обнаруженный на изображении. Для каждого штрихкода можно получить его координаты на входном изображении, а также первичные данные, закодированные в штрихкоде. Кроме того, если сканер штрихкодов смог определить тип данных, закодированных в штрихкоде, вы можете получить объект, содержащий обработанные данные.

Пример:

Swift

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

Objective-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 мегапикселей), что приводит к очень большой задержке без какого-либо улучшения точности. Вместо этого запрашивайте у камеры только тот размер, который необходим для сканирования штрихкодов. Обычно это не более двух мегапикселей.

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

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

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

    Контрольная цифра не поддерживается для ITF и CODE-39.

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