Scanner des codes-barres avec ML Kit sur iOS

Vous pouvez utiliser ML Kit pour reconnaître et décoder les codes-barres.

Essayer

Avant de commencer

  1. Incluez les pods ML Kit suivants dans votre Podfile :
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. Après avoir installé ou mis à jour les pods de votre projet, ouvrez votre projet Xcode à l'aide de son fichier .xcworkspace. ML Kit est compatible avec Xcode 12.4 ou version ultérieure.

Consignes pour les images d'entrée

  • Pour que ML Kit puisse lire avec précision les codes-barres, les images d'entrée doivent contenir des codes-barres représentés par une quantité de données de pixels suffisante.

    Les exigences spécifiques en termes de données de pixels dépendent à la fois du type de code-barres et de la quantité de données encodées dans celui-ci, car de nombreux codes-barres acceptent une charge utile de taille variable. En général, la plus petite unité significative du code-barres doit être d'au moins 2 pixels de large et, pour les codes bidimensionnels, de 2 pixels de haut.

    Par exemple, les codes-barres EAN-13 sont constitués de barres et d'espaces d'une largeur de 1, 2, 3 ou 4 unités. Une image de code-barres EAN-13 doit donc idéalement comporter des barres et des espaces d'au moins 2, 4, 6 et 8 pixels de large. Étant donné qu'un code-barres EAN-13 fait au total 95 unités de large, sa largeur doit être d'au moins 190 pixels.

    Les formats Denser, tels que PDF417, nécessitent des dimensions en pixels plus importantes pour que ML Kit puisse les lire de manière fiable. Par exemple, un code PDF417 peut comporter jusqu'à 34 "mots" de 17 unités sur une seule ligne, d'une largeur minimale de 1 156 pixels.

  • Une mise au point médiocre peut affecter la précision de la numérisation. Si votre application n'obtient pas de résultats acceptables, demandez à l'utilisateur de reprendre l'image.

  • Pour les applications classiques, il est recommandé de fournir une image de meilleure résolution (1 280 x 720 ou 1 920 x 1 080, par exemple), ce qui permet de scanner les codes-barres à une plus grande distance de la caméra.

    Toutefois, dans les applications où la latence est essentielle, vous pouvez améliorer les performances en capturant des images à une résolution inférieure, mais en exigeant que le code-barres représente la majorité de l'image d'entrée. Consultez également la page Conseils pour améliorer les performances en temps réel.

1. Configurer le lecteur de code-barres

Si vous savez quels formats de codes-barres vous souhaitez lire, vous pouvez améliorer la vitesse du lecteur de codes-barres en le configurant pour qu'il n'analyse que ces formats.

Par exemple, pour ne scanner que le code aztec et les codes QR, créez un objet BarcodeScannerOptions comme dans l'exemple suivant:

Swift

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

Les formats suivants sont acceptés :

  • code128
  • code39
  • code93
  • codaBar
  • dataMatrix
  • EAN13
  • EAN8
  • Italie
  • qrCode
  • UPCA
  • UPCE
  • PDF417
  • Aztec

Objective-C

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

Les formats suivants sont acceptés :

  • Code 128 (MLKBarcodeFormatCode128)
  • Code 39 (MLKBarcodeFormatCode39)
  • Code 93 (MLKBarcodeFormatCode93)
  • Codabar (MLKBarcodeFormatCodaBar)
  • Matrice de données (MLKBarcodeFormatDataMatrix)
  • EAN-13 (MLKBarcodeFormatEAN13)
  • EAN-8 (MLKBarcodeFormatEAN8)
  • ITF (MLKBarcodeFormatITF)
  • Code QR (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • Code Aztec (MLKBarcodeFormatAztec)

2. Préparer l'image d'entrée

Pour scanner les codes-barres d'une image, transmettez l'image en tant que UIImage ou CMSampleBufferRef à la méthode process() ou results(in:) de BarcodeScanner:

Créez un objet VisionImage à l'aide d'un UIImage ou d'un CMSampleBuffer.

Si vous utilisez un UIImage, procédez comme suit:

  • Créez un objet VisionImage avec UIImage. Veillez à spécifier le bon .orientation.

    Swift

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

    Objective-C

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

Si vous utilisez un CMSampleBuffer, procédez comme suit:

  • Spécifiez l'orientation des données d'image contenues dans le fichier CMSampleBuffer.

    Pour obtenir l'orientation de l'image:

    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;
      }
    }
          
  • Créez un objet VisionImage à l'aide de l'objet CMSampleBuffer et de l'orientation:

    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. Obtenir une instance de BarcodeScanner

Obtenez une instance 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. Traiter l'image

Transmettez ensuite l'image à la méthode 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. Obtenir des informations à partir d'un code-barres

Si la lecture de code-barres aboutit, le lecteur renvoie un tableau d'objets Barcode. Chaque objet Barcode représente un code-barres détecté dans l'image. Vous pouvez obtenir les coordonnées de délimitation de chaque code-barres dans l'image d'entrée, ainsi que les données brutes encodées par le code-barres. De plus, si le lecteur de code-barres a pu déterminer le type de données encodées par le code-barres, vous pouvez obtenir un objet contenant des données analysées.

Exemple :

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

Conseils pour améliorer les performances en temps réel

Si vous souhaitez scanner des codes-barres dans une application en temps réel, suivez ces consignes pour obtenir les meilleures fréquences d'images:

  • N'enregistrez pas l'entrée avec la résolution native de la caméra. Sur certains appareils, la capture d'entrée à la résolution native produit des images extrêmement grandes (plus de 10 mégapixels), ce qui se traduit par une latence très faible sans aucun avantage en termes de précision. Demandez uniquement la taille à l'appareil photo requis pour la lecture des codes-barres, qui ne dépasse généralement pas 2 mégapixels.

    Toutefois, les préréglages de session de capture nommés (AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium, etc.) ne sont pas recommandés, car ils peuvent être mappés à des résolutions inappropriées sur certains appareils. Utilisez plutôt des préréglages spécifiques tels que AVCaptureSessionPreset1280x720.

    Si la vitesse de numérisation est importante, vous pouvez réduire davantage la résolution de capture de l'image. Toutefois, tenez compte des exigences minimales de taille des codes-barres décrites ci-dessus.

    Si vous essayez de reconnaître les codes-barres d'une séquence d'images vidéo en flux continu, le programme de reconnaissance peut produire des résultats différents d'une image à l'autre. Vous devez attendre d'obtenir une série consécutive de la même valeur pour être sûr de renvoyer un bon résultat.

    Le chiffre de la somme de contrôle n'est pas pris en charge pour ITF et CODE-39.

  • Pour traiter les images vidéo, utilisez l'API synchrone results(in:) du détecteur. Appelez cette méthode à partir de la fonction captureOutput(_, didOutput:from:) de AVCaptureVideoDataOutputSampleBufferDelegate pour obtenir de manière synchrone les résultats de l'image vidéo donnée. Conservez la valeur alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput définie sur true pour limiter les appels au détecteur. Si une nouvelle image vidéo devient disponible alors que le détecteur est en cours d'exécution, elle sera ignorée.
  • Si vous utilisez la sortie du détecteur pour superposer des graphiques sur l'image d'entrée, obtenez d'abord le résultat de ML Kit, puis affichez l'image et la superposition en une seule étape. Ainsi, vous n'effectuez le rendu sur la surface d'affichage qu'une seule fois pour chaque trame d'entrée traitée. Consultez la section updatePreviewOverlayViewWithLastFrame dans l'exemple de démarrage rapide de ML Kit pour en voir un exemple.