Escanea códigos de barras con ML Kit en iOS

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

Probar

Antes de comenzar

  1. Incluye los siguientes pods del ML Kit en tu Podfile:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. Después de instalar o actualizar los Pods de tu proyecto, abre el proyecto de Xcode con su .xcworkspace. El ML Kit es compatible con Xcode 12.4 o versiones posteriores.

Lineamientos para imágenes de entrada

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

    Los requisitos específicos de los datos de píxeles dependen del tipo de código de barras y de la cantidad de datos codificados en él, ya que muchos códigos de barras admiten una carga útil de tamaño variable. En general, la unidad más pequeña de significado del código de barras debe tener al menos 2 píxeles de ancho y, para los códigos de 2 dimensiones, 2 píxeles de alto.

    Por ejemplo, los códigos de barras EAN-13 se componen de barras y espacios de 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 2, 4, 6 y 8 píxeles de ancho. Como un código de barras EAN-13 tiene 95 unidades de ancho en total, este debe tener al menos 190 píxeles de ancho.

    Los formatos más densos, como PDF417, necesitan mayores dimensiones de píxeles para que el ML Kit pueda leerlos 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ían 1,156 píxeles de ancho.

  • Un enfoque de imagen deficiente puede afectar la precisión del escaneo. Si tu app no obtiene resultados aceptables, pídele al usuario que vuelva a capturar la imagen.

  • En aplicaciones típicas, se recomienda proporcionar una imagen de mayor resolución, como 1280 x 720 o 1920 x 1080, que permite que los códigos de barras se puedan escanear a mayor distancia de la cámara.

    Sin embargo, en aplicaciones en las que la latencia es crítica, puedes mejorar el rendimiento si capturas imágenes con una resolución más baja, pero que requieran 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 escáner de código de barras

Si sabes qué formatos de códigos de barras leerás, puedes configurar el escáner de código de barras para que solo escanee esos formatos a fin de mejorar su velocidad.

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

Swift

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

Se admiten los siguientes formatos:

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

Objective‑C

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

Se admiten los siguientes formatos:

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

2. Prepara la imagen de entrada

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

Crea un objeto VisionImage mediante una UIImage o CMSampleBuffer.

Si usas un UIImage, sigue estos pasos:

  • Crea un objeto VisionImage con la UIImage. Asegúrate de especificar el .orientation correcto.

    Swift

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

    Objective‑C

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

Si usas un CMSampleBuffer, sigue estos pasos:

  • Especifica la orientación de los datos de imagen que contiene CMSampleBuffer.

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

    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;
      }
    }
          
  • Crea un objeto VisionImage con el objeto CMSampleBuffer y la orientación:

    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. Cómo obtener una instancia de BarcodeScanner

Obtén una instancia 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. Procesa la imagen

Por último, pasa la imagen al 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. Obtén información de códigos de barras

Si la operación de escaneo de códigos de barras se ejecuta correctamente, el escáner mostrará un array de objetos Barcode. Cada objeto Barcode representa un código de barras que se detectó en la imagen. Para cada código de barras, puedes obtener las coordenadas de los límites en la imagen de entrada, así como los datos sin procesar codificados en el código de barras. Además, si el escáner de código 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 (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;
   }
 }

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:

  • No captures entradas con la resolución nativa de la cámara. En algunos dispositivos, la captura de entradas en la resolución nativa produce imágenes extremadamente grandes (más de 10 megapíxeles), lo que genera una latencia muy baja y no beneficia la precisión. En su lugar, solo solicita a la cámara el tamaño necesario para el escaneo de códigos de barras, que no suele tener más de 2 megapíxeles.

    Sin embargo, no se recomiendan los ajustes predeterminados de sesión de captura nombrados (AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium, etc.), ya que pueden asignarse a 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 imágenes. Sin embargo, ten en cuenta los requisitos mínimos de tamaño de códigos de barras descritos anteriormente.

    Si intentas reconocer códigos de barras de una secuencia de fotogramas de video en streaming, el reconocedor podría producir resultados diferentes de fotograma a fotograma. Debes esperar hasta obtener una serie consecutiva del mismo valor para estar seguro de mostrar un buen resultado.

    El dígito de la suma de comprobación no es compatible con ITF y CODE-39.

  • Para procesar fotogramas de video, usa la API síncrona results(in:) del detector. Llama a este método desde la función captureOutput(_, didOutput:from:) de AVCaptureVideoDataOutputSampleBufferDelegate para obtener resultados de un fotograma de video determinado de manera síncrona. Mantén el alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput como true para limitar las llamadas al detector. Si hay un fotograma de video nuevo disponible mientras se ejecuta el detector, se descartará.
  • Si usas la salida del detector para superponer gráficos en la imagen de entrada, primero obtén el resultado del ML Kit y, luego, procesa la imagen y la superposición en un solo paso. De esta manera, renderizas en la superficie de visualización solo una vez por cada fotograma de entrada procesado. Consulta updatePreviewOverlayViewWithLastFrame en la muestra de inicio rápido del Kit de AA para ver un ejemplo.