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.

Testar

Antes de começar

  1. Inclua os seguintes pods do Kit de ML no seu Podfile:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. Depois de instalar ou atualizar os pods do 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 códigos de barras representados por dados de pixel suficientes.

    Os requisitos específicos de dados de pixel dependem do tipo de código de barras e da quantidade de dados codificados nele, já que muitos códigos de barras são compatíveis com um payload de tamanho variável. Em geral, a menor unidade significativa do código de barras precisa ter pelo menos 2 pixels de largura e, para códigos bidimensionais, 2 pixels de altura.

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

    Formatos mais densos, como PDF417, precisam de dimensões em pixels maiores para que o Kit de ML possa fazer a leitura confiável deles. Por exemplo, um código PDF417 pode ter até 34 "palavras" de 17 unidades em uma única linha, com um ideal de pelo menos 1.156 pixels de largura.

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

  • Para aplicativos típicos, recomendamos fornecer uma imagem de resolução mais alta, como 1.280 x 720 ou 1.920 x 1.080, o que torna os códigos de barras legíveis a uma distância maior da câmera.

    No entanto, em aplicativos em que a latência é crítica, é possível melhorar o desempenho capturando imagens em resolução mais baixa, mas exigindo que o código de barras componha 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 você souber quais formatos de código de barras espera ler, 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 um 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)
  • Código QR (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • Código asteca (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 método 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 em CMSampleBuffer.

    Para ver 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 CMSampleBuffer e a orientação:

    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 um código de barras detectado na imagem. Para cada código de barras, é possível receber as 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 conseguiu determinar o tipo de dados codificados pelo código de barras, você poderá receber um objeto contendo 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

Caso queira ler códigos de barras em um aplicativo em tempo real, siga estas diretrizes para ter as melhores taxas de frames:

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

    As predefinições de sessão de captura nomeadas (AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium e assim por diante) não são recomendadas porque podem ser mapeadas para resoluções inadequadas em alguns dispositivos. Em vez disso, use as predefinições específicas, como AVCaptureSessionPreset1280x720.

    Se a velocidade de leitura for importante, você poderá diminuir ainda mais a resolução da captura de imagem. 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 frames de streaming de vídeo, o reconhecedor poderá produzir resultados diferentes de frame para frame. Espere até receber uma série consecutiva do mesmo valor para ter certeza de que está retornando um bom resultado.

    O dígito do Checksum não é suportado para ITF e CODE-39.

  • Para processar frames de vídeo, use a API síncrona results(in:) do detector. Chame esse método na função captureOutput(_, didOutput:from:) de AVCaptureVideoDataOutputSampleBufferDelegate para receber resultados de forma síncrona do frame de vídeo especificado. Mantenha o alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput 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 na imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem e a 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. Consulte updatePreviewOverlayViewWithLastFrame no exemplo do guia de início rápido do Kit de ML.