在 iOS 上使用 ML Kit 掃描條碼

你可以使用機器學習套件辨識及解碼條碼。

立即體驗

事前準備

  1. 在 Podfile 中加入下列機器學習套件 Pod:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. 安裝或更新專案的 Pod 後,使用 .xcworkspace 開啟 Xcode 專案。Xcode 12.4 以上版本支援機器學習套件。

輸入圖片規範

  • 為了讓機器學習套件能準確讀取條碼,輸入圖片必須包含用來代表充足像素資料的條碼。

    許多像素資料需求都取決於條碼類型和其中編碼的資料量,因為許多條碼都支援可變大小酬載。一般而言,條碼最小的有意義的單位至少應為 2 像素的寬度,而二維代碼的高度應為 2 像素。

    舉例來說,EAN-13 條碼是由 1、2、3 或 4 個單位寬的長條和空格所組成,因此 EAN-13 條碼圖片的長條和空格至少應為 2、4、6 和 8 像素寬度。由於 EAN-13 條碼的總寬度為 95 單位,所以條碼至少應為 190 像素寬。

    PDF417 等 Denser 格式需要較大的像素尺寸,機器學習套件才能穩定讀取。舉例來說,PDF 417 程式碼的單一資料列最多可包含 34 個 17 單位寬的「字詞」,理想情況下寬度至少為 1156 像素。

  • 圖片焦點不佳可能會影響掃描準確度。如果您的應用程式無法取得可接受的結果,請要求使用者擷取圖片。

  • 如果是一般應用程式,則建議提供解析度更高的圖片,例如 1280x720 或 1920x1080,讓掃描碼範圍較遠的相機也能掃描。

    不過,在延遲時間極長的應用程式中,您可以採用較低解析度拍攝圖片,藉此提升效能,但要求條碼會佔大部分的輸入圖像。另請參閱改善即時效能的提示

1. 設定條碼掃描器

如果您知道預期的閱讀格式,您可以將條碼掃描器設定為僅掃描這些格式,藉此提升條碼掃描器的速度。

舉例來說,如果只要掃描 Aztec 程式碼和 QR 圖碼,請建構 BarcodeScannerOptions 物件,如下列範例所示:

Swift

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

系統支援以下格式:

  • 代碼 128
  • 代碼 39
  • 代碼 93
  • CodaBar
  • 資料矩陣
  • EAN13
  • EAN8
  • 義大利
  • qrCode (兌換代碼)
  • UPCA
  • 上衣
  • PDF417
  • Aztec

Objective-C

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

系統支援以下格式:

  • 代碼 128 (MLKBarcodeFormatCode128)
  • 代碼 39 (MLKBarcodeFormatCode39)
  • 代碼 93 (MLKBarcodeFormatCode93)
  • 科達巴 (MLKBarcodeFormatCodaBar)
  • 資料矩陣 (MLKBarcodeFormatDataMatrix)
  • EAN-13 (MLKBarcodeFormatEAN13)
  • EAN-8 (MLKBarcodeFormatEAN8)
  • ITF (MLKBarcodeFormatITF)
  • QR 圖碼 (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • Aztec 代碼 (MLKBarcodeFormatAztec)

2. 準備輸入圖片

如要掃描圖片中的條碼,請將圖片以 UIImageCMSampleBufferRef 的形式傳送至 BarcodeScannerprocess()results(in:) 方法:

使用 UIImageCMSampleBuffer 建立 VisionImage 物件。

如果您使用 UIImage,請按照下列步驟操作:

  • 使用 UIImage 建立 VisionImage 物件。請務必指定正確的 .orientation

    Swift

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

    Objective-C

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

如果您使用 CMSampleBuffer,請按照下列步驟操作:

  • 指定 CMSampleBuffer 中包含的圖片資料方向。

    如何取得圖片方向:

    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;
      }
    }
          
  • 使用 CMSampleBuffer 物件和方向建立 VisionImage 物件:

    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. 取得 BarcodeScanner 的執行個體

取得 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. 處理圖片

接著,將圖片傳送至 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. 從條碼取得資訊

如果條碼掃描作業成功,掃描器會傳回 Barcode 物件陣列。每個 Barcode 物件都代表在圖片中偵測到的條碼。針對每個條碼,您可以在輸入圖片中取得其定界座標,以及由條碼編碼的原始資料。此外,如果條碼掃描器能夠判斷條碼編碼的資料類型,您可以取得包含剖析資料的物件。

例如:

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

改善即時成效的訣竅

如果您想在即時應用程式中掃描條碼,請按照下列指南設定最佳影格速率:

  • 請不要以攝影機的原始解析度擷取輸入。在某些裝置上,以原生解析度擷取輸入會產生非常大 (1000 萬像素以上) 的圖片,因此延遲時間很短,而且對準確率沒有幫助。而是要求從相機執行條碼掃描所需的大小 (通常不超過 200 萬像素)。

    不過,我們不建議使用已擷取的擷取工作階段預設設定,例如 AVCaptureSessionPresetDefaultAVCaptureSessionPresetLowAVCaptureSessionPresetMedium 等,因為這些是對應至某些裝置的不適當解析度。請改用特定預設 (例如 AVCaptureSessionPreset1280x720)。

    如果掃描速度很重要,您可以進一步降低圖片拍攝解析度。但請注意,上述條碼大小下限下限。

    如果您嘗試辨識一系列串流影格的條碼,辨識器可能會產生不同的影格結果。您應等到出現相同值的連續連續序列後,才能夠傳回正確的結果。

    ITF 和 CODE-39 不支援總和檢查碼。

  • 如要處理影格,請使用偵測工具的 results(in:) 同步 API。從 AVCaptureVideoDataOutputSampleBufferDelegatecaptureOutput(_, didOutput:from:) 函式呼叫此方法,即可同步取得特定影片影格的結果。將 AVCaptureVideoDataOutputalwaysDiscardsLateVideoFrames 保留為 true,以限制對偵測工具的呼叫。假如在偵測器執行期間有新的視訊畫面可用,系統就會捨棄該影格。
  • 如果您使用偵測工具的輸出內容,為輸入圖片上的圖像重疊,請先透過 ML Kit 取得結果,然後透過單一步驟算繪圖像和疊加層。如此一來,每個處理的輸入影格只會轉譯一次到顯示途徑一次。如需範例,請參閱 ML Kit 快速入門導覽課程範例中的 updatePreviewOverlayViewWithLastFrame