在 iOS 上使用 ML Kit 掃描條碼

您可以使用ML Kit來識別和解碼條碼。

在你開始之前

  1. 如果您尚未將 Firebase 新增至您的應用程式中,請按照入門指南中的步驟進行操作。
  2. 在 Podfile 中包含 ML Kit 函式庫:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    安裝或更新專案的 Pod 後,請務必使用其.xcworkspace開啟 Xcode 專案。
  3. 在您的應用程式中,導入 Firebase:

    迅速

    import Firebase

    Objective-C

    @import Firebase;

輸入影像指南

  • 為了讓 ML Kit 準確讀取條碼,輸入影像必須包含由足夠像素資料表示的條碼。

    特定的像素資料要求取決於條碼的類型和其中編碼的資料量(因為大多數條碼支援可變長度有效負載)。一般來說,條碼的最小有意義單元應至少為 2 個像素寬(對於二維碼,為 2 個像素高)。

    例如,EAN-13 條碼由 1、2、3 或 4 個單位寬的條和空格組成,因此 EAN-13 條碼影像理想情況下具有至少 2、4、6 和 4 個單位的條和空格。8 像素寬。由於 EAN-13 條碼的總寬度為 95 個單位,因此條碼的寬度應至少為 190 像素。

    更密集的格式(例如 PDF417)需要更大的像素尺寸,ML Kit 才能可靠地讀取它們。例如,PDF417 程式碼在單行中最多可以有 34 個 17 單位寬的“字”,理想情況下寬度至少為 1156 像素。

  • 影像聚焦不良會影響掃描精度。如果您沒有獲得可接受的結果,請嘗試要求使用者重新捕捉影像。

  • 對於典型應用,建議提供更高解析度的影像(例如 1280x720 或 1920x1080),這樣可以從相機較遠的距離檢測條碼。

    然而,在延遲至關重要的應用中,您可以透過以較低解析度擷取影像來提高效能,但要求條碼構成輸入影像的大部分。另請參閱提高即時效能的提示

1. 設定條碼偵測器

如果您知道希望讀取哪些條碼格式,則可以透過將條碼偵測器配置為僅偵測這些格式來提高條碼偵測器的速度。

例如,若要僅偵測 Aztec 程式碼和 QR 程式碼,請建立VisionBarcodeDetectorOptions對象,如下例所示:

迅速

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

支援以下格式:

  • 代碼128
  • 代碼39
  • 代碼93
  • 科達酒吧
  • EAN13
  • EAN8
  • 國際乒聯
  • UPCA
  • 統一教育委員會
  • QR 圖碼
  • PDF417
  • 阿茲特克人
  • 數據矩陣

Objective-C

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

支援以下格式:

  • 代碼 128 ( FIRVisionBarcodeFormatCode128 )
  • 代碼 39 ( FIRVisionBarcodeFormatCode39 )
  • 代碼 93 ( FIRVisionBarcodeFormatCode93 )
  • Codabar( FIRVisionBarcodeFormatCodaBar
  • EAN-13( FIRVisionBarcodeFormatEAN13
  • EAN-8( FIRVisionBarcodeFormatEAN8
  • ITF( FIRVisionBarcodeFormatITF
  • UPC-A( FIRVisionBarcodeFormatUPCA
  • UPC-E( FIRVisionBarcodeFormatUPCE
  • QR 碼 ( FIRVisionBarcodeFormatQRCode )
  • PDF417 ( FIRVisionBarcodeFormatPDF417 )
  • 阿茲特克 ( FIRVisionBarcodeFormatAztec )
  • 資料矩陣( FIRVisionBarcodeFormatDataMatrix

2. 運行條碼檢測器

若要掃描影像中的條碼,請將影像作為UIImageCMSampleBufferRef傳遞給VisionBarcodeDetectordetect(in:)方法:

  1. 取得VisionBarcodeDetector的實例:

    迅速

    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. 使用UIImageCMSampleBufferRef建立VisionImage物件。

    使用UIImage

    1. 如有必要,旋轉影像,使其imageOrientation屬性為.up
    2. 使用正確旋轉的UIImage建立VisionImage物件。不要指定任何旋轉元資料 - 必須使用預設值.topLeft

      迅速

      let image = VisionImage(image: uiImage)

      Objective-C

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

    使用CMSampleBufferRef

    1. 建立一個VisionImageMetadata對象,該對象指定CMSampleBufferRef緩衝區中包含的圖像資料的方向。

      若要取得影像方向:

      迅速

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

      然後,建立元資料物件:

      迅速

      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. 使用CMSampleBufferRef物件和旋轉元資料建立VisionImage物件:

      迅速

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. 然後,將圖像傳遞給detect(in:)方法:

    迅速

    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. 從條碼中獲取訊息

如果條碼識別操作成功,偵測器將傳回VisionBarcode物件的陣列。每個VisionBarcode物件代表在影像中偵測到的條碼。對於每個條碼,您可以獲得其在輸入影像中的邊界座標,以及條碼編碼的原始資料。此外,如果條碼偵測器能夠確定條碼編碼的資料類型,您可以獲得包含解析資料的物件。

例如:

迅速

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

提升即時效能的技巧

如果您想在即時應用程式中掃描條碼,請遵循以下指南以獲得最佳幀速率:

  • 不要以相機的原始解析度捕捉輸入。在某些裝置上,以原始解析度擷取輸入會產生非常大(10+ 百萬像素)的影像,這會導致非常低的延遲,並且對準確性沒有任何好處。相反,僅向相機請求條碼檢測所需的尺寸:通常不超過 200 萬像素。

    但是,不建議使用命名的擷取會話預設( AVCaptureSessionPresetDefaultAVCaptureSessionPresetLowAVCaptureSessionPresetMedium等),因為它們可能會對應到某些裝置上不合適的解析度。相反,請使用特定預設,例如AVCaptureSessionPreset1280x720

    如果掃描速度很重要,您可以進一步降低影像擷取解析度。但是,請記住上述最小條碼尺寸要求。

  • 對檢測器的節流呼叫。如果偵測器運作時有新的視訊幀可用,則丟棄該幀。
  • 如果您使用偵測器的輸出將圖形疊加在輸入影像上,請先從 ML Kit 取得結果,然後一步渲染影像並疊加。透過這樣做,每個輸入幀只需渲染到顯示表面一次。有關範例,請參閱展示範例應用程式中的PreviewOverlayViewFIRDetectionOverlayView類別。