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 ancora aggiunto Firebase alla tua app, segui i passaggi descritti nella guida introduttiva.
  2. Includi le librerie ML Kit nel tuo Podfile:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    Dopo aver installato o aggiornato i pod del progetto, assicurati di aprire il progetto Xcode utilizzando il relativo .xcworkspace.
  3. Nell'app, importa Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

Linee guida per le immagini di input

  • Affinché ML Kit legga con precisione i codici a barre, le immagini di input devono contenere codici a barre rappresentati da dati di pixel sufficienti.

    I requisiti specifici dei dati dei pixel dipendono sia dal tipo di codice a barre sia dalla quantità di dati codificati al suo interno (poiché la maggior parte dei codici a barre supporta un payload di lunghezza variabile). In generale, l'unità minima significativa del codice a barre deve avere una larghezza di almeno 2 pixel (e per i codici 2D, un'altezza di 2 pixel).

    Ad esempio, i codici a barre EAN-13 sono costituiti da barre e spazi di larghezza 1, 2, 3 o 4 unità, pertanto un'immagine di un codice a barre EAN-13 dovrebbe avere barre e spazi di almeno 2, 4, 6 e 8 pixel di larghezza. Poiché un codice a barre EAN-13 ha una larghezza totale di 95 unità, deve avere una larghezza di almeno 190 pixel.

    I formati più densi, come PDF417, richiedono dimensioni dei pixel maggiori per consentire a ML Kit di leggerli in modo affidabile. Ad esempio, un codice PDF417 può avere fino a 34 "parole" di 17 unità di larghezza in una singola riga, che idealmente dovrebbe essere di almeno 1156 pixel di larghezza.

  • 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 standard, è consigliabile fornire un'immagine con una risoluzione superiore (ad esempio 1280 x 720 o 1920 x 1080), che rende i codici a barre rilevabili da una distanza maggiore dalla videocamera.

    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. Consulta 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 prevedi di leggere, puoi migliorare la velocità del rilevatore di codici a barre configurandolo in modo da rilevare solo questi formati.

Ad esempio, per rilevare solo codici Aztec e QR, crea un oggetto VisionBarcodeDetectorOptions come nell'esempio seguente:

Swift

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

Sono supportati i seguenti formati:

  • Code128
  • Code39
  • Code93
  • CodaBar
  • EAN13
  • EAN8
  • ITF
  • UPCA
  • UPCE
  • Codice QR
  • PDF417
  • Azteco
  • DataMatrix

Objective-C

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

Sono supportati i seguenti formati:

  • Code 128 (FIRVisionBarcodeFormatCode128)
  • Code 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)
  • Aztec (FIRVisionBarcodeFormatAztec)
  • Data Matrix (FIRVisionBarcodeFormatDataMatrix)

2. Esegui il rilevamento dei codici a barre

Per scansionare i codici a barre in un'immagine, passa l'immagine come UIImage o CMSampleBufferRef al metodo 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 utilizzare un UIImage:

    1. Se necessario, ruota l'immagine in modo che la sua proprietà imageOrientation sia .up.
    2. Crea un oggetto VisionImage utilizzando il UIImage ruotato correttamente. Non specificare metadati di rotazione. Deve essere utilizzato il valore predefinito .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Per utilizzare un CMSampleBufferRef:

    1. Crea un oggetto VisionImageMetadata che specifichi l'orientamento dei dati immagine contenuti nel 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 dei 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 l'oggetto CMSampleBufferRef e i 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. Ottenere informazioni dai codici a barre

Se l'operazione di riconoscimento del codice a barre va a buon fine, il rilevatore restituisce un array di oggetti VisionBarcode. Ogni oggetto VisionBarcode rappresenta un codice a barre rilevato nell'immagine. Per ogni codice a barre, puoi ottenere le sue coordinate di confine nell'immagine di input, nonché i dati non elaborati codificati dal codice a barre. Inoltre, se il rilevatore di codici a barre è stato in grado di determinare il tipo di dati codificati 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 vuoi scansionare i codici a barre in un'applicazione in tempo reale, segui queste linee guida per ottenere le frequenze frame migliori:

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

    Tuttavia, le preimpostazioni delle sessioni di acquisizione denominate (AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium e così via) non sono consigliate, in quanto possono essere mappate a risoluzioni non idonee su alcuni dispositivi. Utilizza invece le preimpostazioni specifiche, come AVCaptureSessionPreset1280x720.

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

  • Regola le chiamate al rilevatore. Se un nuovo frame video diventa disponibile mentre il rilevatore è in esecuzione, inseriscilo.
  • 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. Per un esempio, consulta le classi previewOverlayView e FIRDetectionOverlayView nell'app di esempio della vetrina.