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.

Trước khi bắt đầu

  1. Nếu bạn chưa thêm Firebase vào ứng dụng của mình, hãy thực hiện bằng cách làm theo hướng dẫn các bước trong hướng dẫn bắt đầu sử dụng.
  2. Thêm các thư viện Bộ công cụ học máy vào Podfile của bạn:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    Sau khi cài đặt hoặc cập nhật Nhóm của dự án, hãy nhớ mở Xcode bằng cách sử dụng .xcworkspace của nó.
  3. Trong ứng dụng của bạn, hãy nhập Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

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á trong đó (vì hầu hết các mã vạch có hỗ trợ tải trọng có độ dài thay đổi). Nói chung, biến thể nhỏ nhất có ý nghĩa đơn vị mã vạch phải có chiều 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 hình ảnh kém có thể ảnh hưởng đến độ chính xác của quét. Nếu bạn không nhận được kết quả có thể chấp nhận được, hãy thử 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), tạo mã vạch phát hiện được ở khoảng cách xa máy ảnh hơn.

    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 phát hiện 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 trình phát hiện mã vạch bằng cách định cấu hình để chỉ phát hiện các định dạng đó.

Ví dụ: để chỉ phát hiện mã Aztec và mã QR, hãy tạo một VisionBarcodeDetectorOptions như trong ví dụ sau:

Swift

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

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

  • Mã 128
  • Mã 39
  • Mã 93
  • Quán bar Coda
  • EAN13 (Mã số EAN13)
  • EAN8
  • CNTT
  • UPCA (Liên minh Châu Âu)
  • UPCE
  • Mã QR
  • PDF417
  • Aztec
  • Ma trận dữ liệu

Objective-C

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

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

  • Mã 128 (FIRVisionBarcodeFormatCode128)
  • Mã 39 (FIRVisionBarcodeFormatCode39)
  • Mã 93 (FIRVisionBarcodeFormatCode93)
  • Tiếng Codabar (FIRVisionBarcodeFormatCodaBar)
  • EAN-13 (FIRVisionBarcodeFormatEAN13)
  • EAN-8 (FIRVisionBarcodeFormatEAN8)
  • ITF (FIRVisionBarcodeFormatITF)
  • UPC-A (FIRVisionBarcodeFormatUPCA)
  • UPC-E (FIRVisionBarcodeFormatUPCE)
  • Mã QR (FIRVisionBarcodeFormatQRCode)
  • PDF417 (FIRVisionBarcodeFormatPDF417)
  • Tiếng Aztec (FIRVisionBarcodeFormatAztec)
  • Ma trận dữ liệu (FIRVisionBarcodeFormatDataMatrix)

2. Chạy trình phát hiện mã vạch

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

  1. Nhận một thực thể của 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. Tạo đối tượng VisionImage bằng UIImage hoặc CMSampleBufferRef.

    Cách sử dụng UIImage:

    1. Nếu cần, hãy xoay hình ảnh để imageOrientation.up.
    2. Tạo đối tượng VisionImage bằng chế độ xoay chính xác UIImage. Không chỉ định bất kỳ siêu dữ liệu xoay vòng nào—mặc định bạn phải sử dụng giá trị .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Cách sử dụng CMSampleBufferRef:

    1. Tạo đối tượng VisionImageMetadata chỉ định của dữ liệu hình ảnh chứa trong Vùng đệm CMSampleBufferRef.

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

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

      Sau đó, hãy tạo đối tượng siêu dữ liệu:

      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. Tạo đối tượng VisionImage bằng Đối tượng CMSampleBufferRef và siêu dữ liệu xoay:

      Swift

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Sau đó, hãy truyền hình ảnh đó vào phương thức 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. Lấy thông tin từ mã vạch

Nếu thao tác nhận dạng mã vạch thành công, trình phát hiện sẽ trả về một mảng Đối tượng VisionBarcode. Mỗi đối tượng VisionBarcode đạ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, nếu trình phát hiện 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 (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;
   }
 }

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 kỳ lớn (10 megapixel), dẫn đến độ trễ rất thấp mà không mang lại lợi ích cho của bạn. Thay vào đó, chỉ yêu cầu kích thước từ máy ảnh bắt buộc để phát hiện 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 ý các yêu cầu tối thiểu về kích thước mã vạch nêu trên.

  • Điều tiết lệnh gọi đến trình phát hiện. Nếu một khung video mới trong khi trình phát hiện đang chạy, hãy thả khung hình.
  • Nếu bạn đang 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. Xem previewOverlayViewFIRDetectionOverlayView trong ứng dụng mẫu Showcase.