Ler códigos de barras com o Kit de ML no iOS

É possível usar o Kit de ML para reconhecer e decodificar códigos de barras.

Faça um teste

Antes de começar

  1. Inclua os seguintes pods do kit de ML no seu Podfile:
    pod 'GoogleMLKit/BarcodeScanning', '15.5.0'
    
  2. Depois de instalar ou atualizar os pods do seu projeto, abra o projeto Xcode usando o .xcworkspace: O Kit de ML é compatível com a versão 12.4 ou mais recente do Xcode.

Diretrizes de imagens de entrada

  • Para que o Kit de ML leia códigos de barras com precisão, as imagens de entrada precisam conter em códigos de barras que sejam representados por dados de pixel suficientes.

    Os requisitos específicos de dados de pixel dependem do tipo de código de barras e a quantidade de dados codificados nele, já que muitos dão suporte a um payload de tamanho variável. Em geral, os menores valores a unidade do código de barras deve ter pelo menos 2 pixels de largura e, por Códigos bidimensionais com 2 pixels de altura.

    Por exemplo, os códigos de barras EAN-13 são compostos de barras e espaços que são 1, 2, 3 ou 4 unidades de largura, de modo que uma imagem de código de barras EAN-13 tenha barras e espaços com pelo menos 2, 4, 6 e 8 pixels de largura. Como um EAN-13 o código de barras tem 95 unidades no total, ele deve ter pelo menos 190 pixels de largura.

    Formatos mais densos, como PDF417, precisam de dimensões em pixels maiores para o kit de ML para lê-los de maneira confiável. Por exemplo, um código PDF417 pode ter até 34 "palavras" de 17 unidades em uma única linha, o que idealmente seria pelo menos 1.156 pixels de largura.

  • Uma imagem com foco inadequado pode afetar a precisão da verificação. Se o app não estiver recebendo resultados aceitáveis, peça ao usuário para recapturar a imagem.

  • Para aplicativos típicos, recomenda-se fornecer uma maior imagem de alta resolução, como 1280 x 720 ou 1920 x 1080, que faz códigos de barras possa ser lido a uma distância maior da câmera.

    No entanto, em aplicativos em que a latência é crítica, é possível melhorar desempenho capturando imagens com resolução mais baixa, mas exigindo que o código de barras constitui a maior parte da imagem de entrada. Consulte também Dicas para melhorar o desempenho em tempo real.

1. Configurar o leitor de código de barras

Se souber quais formatos de código de barras espera ler, você poderá aumentar a velocidade do leitor de código de barras configurando-o para ler apenas esses formatos.

Por exemplo, para ler apenas códigos Aztec e QR, crie uma objeto BarcodeScannerOptions, como no exemplo a seguir:

Swift

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

Os seguintes formatos são compatíveis:

  • code128
  • code39
  • code93
  • codaBar
  • dataMatrix
  • EAN13
  • EAN8
  • ITF
  • qrCode
  • UPCA
  • UPCE
  • PDF417
  • Aztec

Objective-C

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

Os seguintes formatos são compatíveis:

  • Código-128 (MLKBarcodeFormatCode128)
  • Código 39 (MLKBarcodeFormatCode39)
  • Código 93 (MLKBarcodeFormatCode93)
  • Codabar (MLKBarcodeFormatCodaBar)
  • Matriz de dados (MLKBarcodeFormatDataMatrix)
  • EAN-13 (MLKBarcodeFormatEAN13)
  • EAN-8 (MLKBarcodeFormatEAN8)
  • ITF (MLKBarcodeFormatITF)
  • QR code (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • Código Aztec (MLKBarcodeFormatAztec)

2. Preparar a imagem de entrada

Para ler códigos de barras em uma imagem, transmita a imagem como UIImage ou CMSampleBufferRef para o process() ou results(in:) do BarcodeScanner :

Crie um objeto VisionImage usando um UIImage ou um CMSampleBuffer.

Se você usa um UIImage, siga estas etapas:

  • Crie um objeto VisionImage com o UIImage. Especifique o .orientation correto.

    Swift

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

    Objective-C

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

Se você usa um CMSampleBuffer, siga estas etapas:

  • Especifique a orientação dos dados da imagem contidos no CMSampleBuffer:

    Para saber qual é a orientação da imagem:

    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;
      }
    }
          
  • Crie um objeto VisionImage usando o Objeto e orientação 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. Acessar uma instância do BarcodeScanner

Receba uma instância de 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. Processar a imagem

Em seguida, transmita a imagem para o método 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. Receber informações de códigos de barras

Se a operação de leitura do código de barras for bem-sucedida, o leitor retornará uma matriz de Objetos Barcode. Cada objeto Barcode representa uma código de barras detectado na imagem. Para cada código de barras, você encontra coordenadas delimitadoras na imagem de entrada, bem como os dados brutos codificados pelo código de barras. Além disso, se o leitor de código de barras tiver conseguido determinar o tipo de dados codificado pelo código de barras, você pode obter um objeto que contém dados analisados.

Exemplo:

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

Dicas para melhorar o desempenho em tempo real

Se você quiser ler códigos de barras em um aplicativo em tempo real, siga estas diretrizes para obter as melhores taxas de quadros:

  • Não capture a entrada na resolução nativa da câmera. Em alguns dispositivos, a captura de entradas na resolução nativa produz volumes extremamente grandes (10+ megapixels), o que resulta em uma latência muito baixa, sem nenhum benefício para precisão. Em vez disso, solicite apenas o tamanho necessário da câmera. para leitura de código de barras, que normalmente não tem mais de 2 megapixels.

    As predefinições da sessão de captura nomeada, AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium entre outras), não são recomendadas, já que podem ser mapeadas e resoluções inadequadas em alguns dispositivos. Em vez disso, use as predefinições específicas como AVCaptureSessionPreset1280x720.

    Se a velocidade de leitura for importante, diminua ainda mais a captura da imagem e resolução. No entanto, lembre-se dos requisitos mínimos de tamanho de código de barras descritos acima.

    Se você estiver tentando reconhecer códigos de barras de uma sequência de streaming quadros de vídeo, o reconhecedor pode produzir resultados diferentes de quadro a frame. Aguarde até receber uma série consecutiva do mesmo para ter certeza de que está retornando um bom resultado.

    O dígito da soma de verificação não é compatível com ITF e CODE-39.

  • Para processar frames de vídeo, use a API síncrona results(in:) do detector. Ligação esse método da AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:) para receber resultados do vídeo fornecido de forma síncrona frame. Manter de AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames como true para limitar as chamadas ao detector. Se um novo frame de vídeo ficar disponível enquanto o detector estiver em execução, ele será descartado.
  • Se você usar a saída do detector para sobrepor elementos gráficos a imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem e sobreposição em uma única etapa. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada frame de entrada processado. Veja a classe updatePreviewOverlayViewWithLastFrame na amostra do guia de início rápido do Kit de ML.