Escanea códigos de barras con ML Kit en iOS

Puedes usar ML Kit para reconocer y decodificar códigos de barras.

Antes de comenzar

  1. Si aún no agregaste Firebase a tu app, sigue los pasos en la guía de introducción para hacerlo.
  2. Incluye las bibliotecas del ML Kit en tu Podfile:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    Después de instalar o actualizar los Pods de tu proyecto, asegúrate de abrir el proyecto de Xcode con su .xcworkspace.
  3. En tu app, importa Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

Lineamientos para imágenes de entrada

  • Para que el Kit de AA reconozca códigos de barras con exactitud, las imágenes de entrada deben contener códigos de barras representados con datos de píxeles suficientes.

    Los requisitos específicos de datos de píxeles dependen del tipo de código de barras y de la cantidad de datos codificados en él (ya que la mayoría de los códigos de barras admiten una carga útil de longitud variable). Por lo general, la unidad mínima de significado de un código de barras debe tener al menos 2 píxeles de ancho (y en códigos de 2 dimensiones, también 2 píxeles de altura).

    Por ejemplo, los códigos de barras EAN-13 contienen barras y espacios con 1, 2, 3 o 4 unidades de ancho, por lo que una imagen de código de barras EAN-13 tiene, idealmente, barras y espacios de al menos 2, 4, 6 y 8 píxeles de ancho. Debido a que un código de barras EAN-13 tiene un ancho de 95 unidades en total, el código de barras deberá tener al menos 190 píxeles de ancho.

    Los formatos más densos, como PDF417, requieren mayores dimensiones de píxeles para que el Kit de AA pueda leerlas de forma confiable. Por ejemplo, un código PDF417 puede tener hasta 34 “palabras” de 17 unidades de ancho en una sola fila, que idealmente tendrá un ancho de 1156 píxeles.

  • Un enfoque de imagen deficiente puede afectar la exactitud del escaneo. Si no obtienes resultados aceptables, intenta pedirle al usuario que vuelva a capturar la imagen.

  • Para aplicaciones típicas, se recomienda proporcionar una imagen de mayor resolución (como 1280 x 720 o 1920 x 1080), lo que hace que los códigos de barras sean detectables a mayor distancia de la cámara.

    Sin embargo, en aplicaciones en las que la latencia es fundamental, puedes mejorar el rendimiento si capturas imágenes con una resolución más baja, en las que el código de barras constituya la mayor parte de la imagen de entrada. Consulta también Sugerencias para mejorar el rendimiento en tiempo real.

1. Configura el detector de códigos de barras

Si sabes qué formatos de códigos de barras leerás, puedes configurar el detector de códigos de barras para que solo detecte esos formatos a fin de aumentar su velocidad.

Por ejemplo, para detectar solo códigos QR y Aztec, crea un objeto VisionBarcodeDetectorOptions como el del siguiente ejemplo:

Swift

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

Se admiten los siguientes formatos:

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

Objective-C

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

Se admiten los siguientes formatos:

  • 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)
  • Código QR (FIRVisionBarcodeFormatQRCode)
  • PDF417 (FIRVisionBarcodeFormatPDF417)
  • Aztec (FIRVisionBarcodeFormatAztec)
  • Data Matrix (FIRVisionBarcodeFormatDataMatrix)

2. Ejecuta el detector códigos de barras

Para escanear códigos de barras en una imagen, pasa la imagen como una UIImage o una CMSampleBufferRef al método detect(in:) de VisionBarcodeDetector:

  1. Obtén una instancia 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. Crea un objeto VisionImage mediante una UIImage o CMSampleBufferRef.

    Para usar una UIImage, debes hacer lo siguiente:

    1. Si es necesario, rota la imagen para que la propiedad imageOrientation sea .up.
    2. Crea un objeto VisionImage mediante una UIImage que se haya rotado adecuadamente. No especifiques los metadatos de rotación. Se debe usar el valor predeterminado .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Para usar una CMSampleBufferRef, debes hacer lo siguiente:

    1. Crea un objeto VisionImageMetadata que especifique la orientación de los datos de la imagen contenidos en el búfer CMSampleBufferRef.

      Para obtener la orientación de la imagen, haz lo siguiente:

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

      Luego crea el objeto de metadatos de esta manera:

      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. Crea un objeto VisionImage a través del objeto CMSampleBufferRef y los metadatos de rotación:

      Swift

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Por último, pasa la imagen al 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. Obtén información de códigos de barras

Si la operación de reconocimiento de código de barras se ejecuta correctamente, el detector muestra un arreglo de objetos VisionBarcode. Cada objeto VisionBarcode representa un código de barras que se detectó en la imagen. Para cada código de barras, puedes obtener las coordenadas de sus límites en la imagen de entrada, junto con los datos sin procesar codificados en el código de barras. Además, si el detector de códigos de barras pudo determinar el tipo de datos codificados en el código de barras, puedes obtener un objeto que contenga los datos analizados.

Por ejemplo:

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

Sugerencias para mejorar el rendimiento en tiempo real

Si quieres escanear códigos de barras en una aplicación en tiempo real, sigue estos lineamientos para lograr la mejor velocidad de fotogramas por segundo:

  • No captures imágenes de entrada con la resolución nativa de la cámara. En algunos dispositivos, la captura en resolución nativa produce imágenes extremadamente grandes (más de 10 megapíxeles), lo que da como resultado una latencia muy pobre y una exactitud baja. En lugar de eso, solicita a la cámara el tamaño requerido para la detección de códigos de barras: por lo general, no más de 2 megapíxeles.

    Sin embargo, no se recomiendan los ajustes predeterminados de sesión de captura (AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium, etc.), ya que pueden dar como resultado resoluciones inadecuadas en algunos dispositivos. En su lugar, usa los ajustes predeterminados específicos, como AVCaptureSessionPreset1280x720.

    Si la velocidad de escaneo es importante, puedes reducir aún más la resolución de captura de imagen. Sin embargo, ten en cuenta los requisitos mínimos de tamaño de códigos de barras descritos anteriormente.

  • Regula las llamadas al detector. Si hay un fotograma de video nuevo disponible mientras se ejecuta el detector, ignora ese fotograma.
  • Si estás usando la salida del detector para superponer gráficos en la imagen de entrada, primero obtén el resultado de la detección de ML Kit y, luego, procesa la imagen y la superposición en un solo paso. De esta manera, procesas la superficie de visualización solo una vez por cada fotograma de entrada. Consulta las clases previewOverlayView y FIRDetectionOverlayView en la app de muestra para ver un ejemplo.