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.

Consulte o app de demonstração do Material Design do Kit de ML e a amostra do guia de início rápido do Kit de ML (links em inglês) no GitHub para ver exemplos da API em uso.

Antes de começar

  1. Se você ainda não adicionou o Firebase ao seu app, siga as etapas no guia de primeiros passos.
  2. Inclua as bibliotecas do Kit de ML no seu Podfile:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    Depois de instalar ou atualizar os pods do seu projeto, abra o projeto do Xcode usando o .xcworkspace.
  3. Importe o Firebase para seu app:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

Diretrizes de imagem de entrada

  • Para que o Kit de ML leia códigos de barras com precisão, as imagens de entrada devem 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 nele codificados (já que a maioria dos códigos de barras é compatível com payload de comprimento variável). Em geral, a menor unidade significativa do código de barras deve 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 de barras e espaços de 1, 2, 3 ou 4 unidades de largura, portanto, uma imagem de código de barras EAN-13 possui barras e espaços de, no mínimo, 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 o PDF417, precisam de dimensões em pixels maiores para que o Kit de ML possa ler de forma confiável. Por exemplo, um código PDF417 pode ter até 34 "palavras" de 17 unidades em uma única linha, o que, idealmente, teria pelo menos 1.156 pixels de largura.

  • Uma imagem com foco inadequado pode prejudicar a precisão. Se você não estiver conseguindo resultados aceitáveis, peça para o usuário recapturar a imagem.

  • Para aplicativos típicos, recomenda-se fornecer uma imagem de resolução mais alta (como 1280 x 720 ou 1920 x 1080), que detecta códigos de barras 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 com resolução mais baixa, mas exigindo que o código de barras compacte a maior parte da imagem de entrada. Consulte também Dicas para melhorar o desempenho em tempo real.

1. Configurar o detector de código de barras

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

Por exemplo, para detectar apenas o código Aztec e os QR codes, crie um objeto VisionBarcodeDetectorOptions como no exemplo a seguir:

Swift

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

Os seguintes formatos são compatíveis:

  • Code128
  • Code39
  • Code93
  • CodaBar
  • EAN13
  • EAN8
  • ITF
  • UPCA
  • UPCE
  • QRCode
  • PDF417
  • Aztec
  • DataMatrix

Objective-C

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

Os seguintes formatos são compatíveis:

  • Code 128 (FIRVisionBarcodeFormatCode128)
  • Code 39 (FIRVisionBarcodeFormatCode39)
  • Code 93 (FIRVisionBarcodeFormatCode93)
  • Codabar (FIRVisionBarcodeFormatCodaBar)
  • EAN-13 (FIRVisionBarcodeFormatEAN13)
  • EAN-8 (FIRVisionBarcodeFormatEAN8)
  • ITF (FIRVisionBarcodeFormatITF)
  • UPC-A (FIRVisionBarcodeFormatUPCA)
  • UPC-E (FIRVisionBarcodeFormatUPCE)
  • QR Code (FIRVisionBarcodeFormatQRCode)
  • PDF417 (FIRVisionBarcodeFormatPDF417)
  • Aztec (FIRVisionBarcodeFormatAztec)
  • Data Matrix (FIRVisionBarcodeFormatDataMatrix)

2. Executar o detector de código de barras

Para ler códigos de barras em uma imagem, transfira a imagem como UIImage ou CMSampleBufferRef para o detect(in:) do VisionBarcodeDetector :

  1. Crie uma instância de VisionBarcodeDetector:

    Swift

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

    Objective-C

    FIRVision *vision = [FIRVision vision];
    FIRVisionBarcodeDetector *barcodeDetector = [vision barcodeDetector];
    // Or, to change the default settings:
    // FIRVisionBarcodeDetector *barcodeDetector =
    //     [vision barcodeDetectorWithOptions:options];
    
  2. Crie um objeto VisionImage usando um UIImage ou um CMSampleBufferRef.

    Para usar uma UIImage:

    1. Se necessário, gire a imagem para que a propriedade imageOrientation seja definida como .up.
    2. Crie um objeto VisionImage usando a UIImage girada corretamente. Não especifique nenhum metadado de rotação. É preciso usar o valor padrão .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Para usar uma CMSampleBufferRef:

    1. Crie um objeto VisionImageMetadata que especifique a orientação dos dados de imagem contidos no buffer CMSampleBufferRef.

      Para obter a orientação da imagem:

      Swift

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

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

      Em seguida, crie o objeto de metadados:

      Swift

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

      Objective-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. Crie um objeto VisionImage usando o CMSampleBufferRef e os metadados de rotação:

      Swift

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Em seguida, transmita a imagem para o método detect(in:):

    Swift

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

    Objective-C

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

3. Receber informações de códigos de barras

Se a operação de reconhecimento do código de barras for bem-sucedida, o detector retornará uma matriz de objetos VisionBarcode. Cada objeto VisionBarcode representa um código de barras que foi 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 no código de barras. Além disso, se o detector de código de barras tiver sido capaz de determinar o tipo de dados codificados pelo código de barras, será possível receber um objeto contendo dados analisados.

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

Dicas para melhorar o desempenho em tempo real

Caso você queira ler códigos de barra em um aplicativo em tempo real, siga estas diretrizes para obter as melhores taxas de frames:

  • Não capture a entrada na resolução nativa da câmera. Em alguns dispositivos, a captura da entrada na resolução nativa produz imagens extremamente grandes (mais de 10 megapixels), o que resulta em uma 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 detecção do código de barras: normalmente, não mais que 2 megapixels.

    As predefinições da sessão de captura nomeada — AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium e assim por diante — não são recomendadas, uma vez que 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, tenha em mente os requisitos mínimos de tamanho do código de barras descritos acima.

  • Limite as chamadas ao detector. Se um novo quadro de vídeo for disponibilizado enquanto o detector estiver em execução, descarte o quadro.
  • Se você estiver usando 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. Consulte as classes previewOverlayView e FIRDetectionOverlayView no app de amostra da demonstração para ver um exemplo.

Próximas etapas

Consulte o app de demonstração do Material Design do Kit de ML e a amostra do guia de início rápido do Kit de ML (links em inglês) no GitHub para ver exemplos da API em uso.