Quét mã vạch bằng Bộ công cụ học máy trên iOS

Bạn có thể dùng Bộ công cụ học máy để nhận dạng và giải mã mã vạch.

Dùng thử

Trước khi bắt đầu

  1. Đưa các nhóm Bộ công cụ học máy sau đây vào Podfile của bạn:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. Sau khi bạn cài đặt hoặc cập nhật Nhóm của dự án, hãy mở dự án Xcode của bạn bằng cách sử dụng .xcworkspace. Bộ công cụ học máy được hỗ trợ trong Xcode phiên bản 12.4 trở lên.

Nguyên tắc nhập hình ảnh

  • Để Bộ công cụ học máy đọc được mã vạch chính xác, hình ảnh đầu vào phải chứa mã vạch được thể hiện bằng đủ dữ liệu pixel.

    Các yêu cầu cụ thể về dữ liệu pixel phụ thuộc vào cả loại mã vạch và số lượng dữ liệu được mã hoá trên đó, vì có nhiều mã vạch hỗ trợ tải trọng có kích thước thay đổi. Nhìn chung, biến thể nhỏ nhất có ý nghĩa đơn vị mã vạch phải rộng tối thiểu là 2 pixel và Mã 2 chiều, cao 2 pixel.

    Ví dụ: mã vạch EAN-13 được tạo thành từ các thanh và khoảng trắng là 1, 2, 3 hoặc 4 đơn vị rộng, vì vậy hình ảnh mã vạch EAN-13 lý tưởng là có các thanh và không gian rộng ít nhất 2, 4, 6 và 8 pixel. Vì EAN-13 mã vạch phải có tổng chiều rộng là 95 đơn vị, mã vạch phải tối thiểu là 190 pixel.

    Các định dạng mật độ cao hơn, chẳng hạn như PDF417, cần kích thước pixel lớn hơn cho Bộ công cụ học máy để đọc các thông tin này một cách chính xác. Ví dụ: mã PDF417 có thể có tối đa 34 "từ" rộng 17 đơn vị trong một hàng duy nhất, tốt nhất là trong ít nhất một hàng Rộng 1156 pixel.

  • Tiêu điểm ảnh kém có thể ảnh hưởng đến độ chính xác của quét. Nếu ứng dụng của bạn không nhận được kết quả có thể chấp nhận được, hãy yêu cầu người dùng chụp lại hình ảnh.

  • Đối với các ứng dụng thông thường, bạn nên cung cấp hình ảnh có độ phân giải, chẳng hạn như 1280x720 hoặc 1920x1080, giúp tạo mã vạch quét được từ một khoảng cách lớn hơn từ máy ảnh.

    Tuy nhiên, trong các ứng dụng có độ trễ là rất quan trọng, bạn có thể cải thiện bằng cách chụp ảnh ở độ phân giải thấp hơn, nhưng đòi hỏi mã vạch chiếm phần lớn hình ảnh đầu vào. Xem thêm Mẹo cải thiện hiệu suất theo thời gian thực.

1. Định cấu hình trình quét mã vạch

Nếu biết định dạng mã vạch nào bạn muốn đọc, bạn có thể cải thiện tốc độ của máy quét mã vạch bằng cách định cấu hình để chỉ quét những định dạng đó.

Ví dụ: để chỉ quét mã Aztec và mã QR, hãy tạo một BarcodeScannerOptions như trong ví dụ sau:

Swift

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

Các định dạng sau được hỗ trợ:

  • code128
  • code39
  • code93
  • codaBar
  • dataMatrix
  • EAN13
  • EAN8
  • ITF
  • qrCode
  • UPCA
  • UPCE
  • PDF417
  • aztec

Objective-C

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

Các định dạng sau được hỗ trợ:

  • Mã-128 (MLKBarcodeFormatCode128)
  • Mã-39 (MLKBarcodeFormatCode39)
  • Mã-93 (MLKBarcodeFormatCode93)
  • Tiếng Codabar (MLKBarcodeFormatCodaBar)
  • Ma trận dữ liệu (MLKBarcodeFormatDataMatrix)
  • EAN-13 (MLKBarcodeFormatEAN13)
  • EAN-8 (MLKBarcodeFormatEAN8)
  • ITF (MLKBarcodeFormatITF)
  • Mã QR (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • Mã Aztec (MLKBarcodeFormatAztec)

2. Chuẩn bị hình ảnh đầu vào

Để quét mã vạch trong hình ảnh, hãy truyền hình ảnh đó dưới dạng UIImage hoặc CMSampleBufferRef đến process() hoặc results(in:) của BarcodeScanner phương thức:

Tạo đối tượng VisionImage bằng UIImage hoặc CMSampleBuffer.

Nếu bạn sử dụng UIImage, hãy làm theo các bước sau:

  • Tạo đối tượng VisionImage bằng UIImage. Hãy nhớ chỉ định đúng .orientation.

    Swift

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

    Objective-C

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

Nếu bạn sử dụng CMSampleBuffer, hãy làm theo các bước sau:

  • Chỉ định hướng của dữ liệu hình ảnh có trong CMSampleBuffer.

    Cách lấy hướng ảnh:

    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;
      }
    }
          
  • Tạo đối tượng VisionImage bằng cách sử dụng Đối tượng và hướng CMSampleBuffer:

    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. Tải một bản sao của BarcodeScanner

Nhận một thực thể của 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. Xử lý hình ảnh

Sau đó, hãy truyền hình ảnh đó vào phương thức 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. Lấy thông tin từ mã vạch

Nếu thao tác quét mã vạch thành công, máy quét sẽ trả về một mảng Đối tượng Barcode. Mỗi đối tượng Barcode đại diện cho một mã vạch phát hiện được trong hình ảnh. Đối với mỗi mã vạch, bạn có thể lấy giới hạn toạ độ trong hình ảnh đầu vào, cũng như dữ liệu thô được mã hoá bởi mã vạch. Ngoài ra, liệu trình quét mã vạch có thể xác định loại dữ liệu được mã hoá bằng mã vạch, bạn có thể nhận được một đối tượng chứa dữ liệu đã phân tích cú pháp.

Ví dụ:

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

Mẹo cải thiện hiệu suất theo thời gian thực

Nếu bạn muốn quét mã vạch trong một ứng dụng theo thời gian thực, hãy làm theo các bước sau để đạt được tốc độ khung hình tốt nhất:

  • Đừng ghi lại dữ liệu đầu vào ở độ phân giải gốc của máy ảnh. Trên một số thiết bị, thu thập đầu vào ở độ phân giải gốc tạo ra kích thước cực lớn (10 megapixel), dẫn đến độ trễ rất thấp mà không mang lại lợi ích cho sự chính xác. Thay vào đó, bạn chỉ cần yêu cầu kích thước từ máy ảnh cần thiết để quét mã vạch, thường không quá 2 megapixel.

    Các giá trị đặt trước cho phiên chụp được đặt tên: AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium v.v.)—tuy nhiên, bạn không nên dùng vì chúng có thể ánh xạ đến độ phân giải không phù hợp trên một số thiết bị. Thay vào đó, hãy dùng các giá trị đặt trước cụ thể chẳng hạn như AVCaptureSessionPreset1280x720.

    Nếu tốc độ quét là quan trọng, bạn có thể giảm tốc độ chụp ảnh hơn nữa độ phân giải. Tuy nhiên, hãy lưu ý đến các yêu cầu tối thiểu về kích thước mã vạch nêu trên.

    Nếu bạn đang cố gắng nhận dạng mã vạch trong một chuỗi phát trực tuyến khung hình video, trình nhận dạng có thể cho ra các kết quả khác nhau giữa các khung hình khung. Bạn nên đợi cho đến khi nhận được một chuỗi dữ liệu giống nhau liên tiếp để tự tin rằng mình đang trả về kết quả tốt.

    Chữ số tổng kiểm không được hỗ trợ cho ITF và CODE-39.

  • Để xử lý khung hình video, hãy sử dụng API đồng bộ results(in:) của trình phát hiện. Gọi điện phương thức này từ Điều khoản dịch vụ và Chính sách quyền riêng tư của AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:) để nhận kết quả một cách đồng bộ từ video đã cho khung. Giữ của AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames dưới dạng true để điều tiết lệnh gọi đến trình phát hiện. Nếu một khách hàng mới khung hình video sẽ bị loại bỏ trong khi trình phát hiện đang chạy.
  • Nếu bạn sử dụng đầu ra của trình phát hiện để phủ đồ hoạ lên hình ảnh đầu vào, trước tiên hãy lấy kết quả từ Bộ công cụ học máy, sau đó kết xuất hình ảnh và phủ lên trên trong một bước duy nhất. Khi làm vậy, bạn sẽ kết xuất lên giao diện màn hình một lần cho mỗi khung đầu vào đã xử lý. Hãy xem lớp updatePreviewOverlayViewWithLastFrame trong mẫu bắt đầu nhanh của Bộ công cụ học máy.