Scansione dei codici a barre con ML Kit su iOS

Puoi utilizzare ML Kit per riconoscere e decodificare i codici a barre.

Prima di iniziare

  1. Se non hai già aggiunto Firebase alla tua app, puoi farlo seguendo le istruzioni riportate in passaggi nella Guida introduttiva.
  2. Includi le librerie del kit ML nel tuo podfile:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    Dopo aver installato o aggiornato i pod del progetto, assicurati di aprire Xcode utilizzando il suo .xcworkspace.
  3. Nell'app, importa Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

Linee guida per le immagini di input

  • Affinché ML Kit possa leggere accuratamente i codici a barre, le immagini di input devono contenere codici a barre rappresentati da un numero sufficiente di dati di pixel.

    I requisiti specifici dei dati relativi ai pixel dipendono da entrambi i tipi di il codice a barre e la quantità di dati codificati al suo interno (dato che la maggior parte dei codici a barre supporta un payload di lunghezza variabile). In generale, la variante più piccola dell'unità di misura del codice a barre deve essere larga almeno 2 pixel (e per codici bidimensionali, altezza 2 pixel).

    Ad esempio, i codici a barre EAN-13 sono composti da barre e spazi che sono 1, 2, 3 o 4 unità di larghezza, quindi un'immagine con codice a barre EAN-13 idealmente contiene barre e spazi di almeno 2, 4, 6 e 8 pixel di larghezza. Poiché un EAN-13 il codice a barre è largo in totale 95 unità, il codice a barre deve essere almeno 190 pixel di larghezza.

    I formati più densi, come PDF417, richiedono dimensioni in pixel maggiori per ML Kit per leggerli in modo affidabile. Ad esempio, un codice PDF417 può avere fino a 34 "parole" di 17 unità di un'unica riga, il che sarebbe idealmente Larghezza 1156 pixel.

  • Una messa a fuoco scadente dell'immagine può influire sulla precisione della scansione. Se non ottieni risultati accettabili, prova a chiedere all'utente di acquisire di nuovo l'immagine.

  • Per le applicazioni tipiche, si consiglia di fornire una maggiore un'immagine con risoluzione massima (ad esempio 1280 x 720 o 1920 x 1080), che crea i codici a barre rilevabile da una distanza maggiore dalla fotocamera.

    Tuttavia, nelle applicazioni in cui la latenza è fondamentale, puoi migliorare le prestazioni acquisendo immagini a una risoluzione inferiore, ma richiedendo che il codice a barre rappresenti la maggior parte dell'immagine di input. Vedi anche Suggerimenti per migliorare il rendimento in tempo reale.

1. Configurare il rilevatore di codici a barre

Se sai quali formati di codici a barre ti aspetti di leggere, puoi migliorare la velocità del rilevatore di codici a barre configurandolo per rilevare solo quei formati.

Ad esempio, per rilevare solo il codice Azteca e i codici QR, crea un VisionBarcodeDetectorOptions come nell'oggetto nell'esempio seguente:

Swift

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

Sono supportati i seguenti formati:

  • Codice128
  • Codice39
  • Codice93
  • CodaBar
  • EAN13
  • EAN8
  • ITF
  • UPCA
  • UPCE
  • Codice QR
  • PDF417
  • Azteco
  • Matrice di dati

Objective-C

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

Sono supportati i seguenti formati:

  • Codice 128 (FIRVisionBarcodeFormatCode128)
  • Codice 39 (FIRVisionBarcodeFormatCode39)
  • Codice 93 (FIRVisionBarcodeFormatCode93)
  • Codabar (FIRVisionBarcodeFormatCodaBar)
  • EAN-13 (FIRVisionBarcodeFormatEAN13)
  • EAN-8 (FIRVisionBarcodeFormatEAN8)
  • ITF (FIRVisionBarcodeFormatITF)
  • UPC-A (FIRVisionBarcodeFormatUPCA)
  • UPC-E (FIRVisionBarcodeFormatUPCE)
  • Codice QR (FIRVisionBarcodeFormatQRCode)
  • PDF417 (FIRVisionBarcodeFormatPDF417)
  • Azteco (FIRVisionBarcodeFormatAztec)
  • Matrice di dati (FIRVisionBarcodeFormatDataMatrix)

2. Attiva il rilevatore di codici a barre

Per scansionare i codici a barre in un'immagine, passa l'immagine come UIImage o CMSampleBufferRef a detect(in:) di VisionBarcodeDetector :

  1. Recupera un'istanza di 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 oggetto VisionImage utilizzando un UIImage o un CMSampleBufferRef.

    Per usare un UIImage:

    1. Se necessario, ruota l'immagine in modo che imageOrientation è .up.
    2. Crea un oggetto VisionImage utilizzando l'oggetto ruotato correttamente UIImage. Non specificare alcun metadato di rotazione (l'impostazione predefinita) è necessario utilizzare il valore .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Per usare un CMSampleBufferRef:

    1. Crea un oggetto VisionImageMetadata che specifichi l'orientamento dei dati dell'immagine contenuti buffer CMSampleBufferRef.

      Per ottenere l'orientamento dell'immagine:

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

      Quindi, crea l'oggetto metadati:

      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 oggetto VisionImage utilizzando il metodo Oggetto CMSampleBufferRef e metadati di rotazione:

      Swift

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Quindi, passa l'immagine al metodo 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. Ricevere informazioni da codici a barre

Se il riconoscimento del codice a barre ha esito positivo, il rilevatore restituisce un array di VisionBarcode oggetti. Ogni oggetto VisionBarcode rappresenta un del codice a barre rilevato nell'immagine. Per ogni codice a barre, puoi ottenere il suo le coordinate di delimitazione nell'immagine di input, nonché i dati non elaborati codificati codice a barre. Inoltre, se il rilevatore di codici a barre è riuscito a determinare il tipo di dati, codificato dal codice a barre, puoi ottenere un oggetto contenente i dati analizzati.

Ad esempio:

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

Suggerimenti per migliorare il rendimento in tempo reale

Se desideri eseguire la scansione dei codici a barre in un'applicazione in tempo reale, procedi nel seguente modo: linee guida per ottenere le migliori frequenze fotogrammi:

  • Non acquisire input alla risoluzione nativa della videocamera. Su alcuni dispositivi, l'input alla risoluzione nativa produce risultati estremamente grandi (10+ megapixel), il che genera una latenza molto scarsa senza alcun vantaggio la precisione. Richiedi invece alla fotocamera solo le dimensioni richieste per il rilevamento del codice a barre: in genere non più di 2 megapixel.

    Le preimpostazioni della sessione di acquisizione denominate: AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium e così via), tuttavia, non sono consigliati in quanto possono essere mappati risoluzioni non adatte su alcuni dispositivi. Usa invece i predefiniti specifici ad esempio AVCaptureSessionPreset1280x720.

    Se la velocità di scansione è importante, puoi ridurre ulteriormente l'acquisizione delle immagini risoluzione del problema. Tuttavia, tieni presente i requisiti minimi per le dimensioni del codice a barre. descritti sopra.

  • Limita le chiamate al rilevatore. Se un nuovo fotogramma disponibili mentre il rilevatore è in esecuzione, abbandona il frame.
  • Se utilizzi l'output del rilevatore per sovrapporre la grafica all'immagine di input, ottieni prima il risultato da ML Kit, poi esegui il rendering dell'immagine e la sovrapposizione in un unico passaggio. In questo modo, esegui il rendering sulla superficie di visualizzazione solo una volta per ogni frame di input. Guarda previewOverlayView. e FIRDetectionOverlayView nell'app di esempio Showcase.