Quét mã vạch bằng ML Kit trên iOS

Bạn có thể sử dụng ML Kit để 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 làm như vậy bằng cách làm theo các bước trong hướng dẫn bắt đầu .
  2. Bao gồm các thư viện ML Kit trong Podfile của bạn:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    Sau khi bạn cài đặt hoặc cập nhật Pod của dự án, hãy nhớ mở dự án Xcode của bạn bằng cách sử dụng .xcworkspace .
  3. Trong ứng dụng của bạn, hãy nhập Firebase:

    Nhanh

    import Firebase

    Mục tiêu-C

    @import Firebase;

Hướng dẫn nhập hình ảnh

  • Để Bộ ML đọc chính xác mã vạch, 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 về dữ liệu pixel cụ thể phụ thuộc vào cả loại mã vạch và lượng dữ liệu được mã hóa trong đó (vì hầu hết các mã vạch đều hỗ trợ tải trọng có độ dài thay đổi). Nói chung, đơn vị có ý nghĩa nhỏ nhất của mã vạch phải rộng tối thiểu 2 pixel (và đối với mã 2 chiều, chiều cao là 2 pixel).

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

    Các định dạng dày đặc hơn, chẳng hạn như PDF417, cần kích thước pixel lớn hơn để ML Kit có thể đọc chúng một cách đáng tin cậy. Ví dụ: mã PDF417 có thể có tối đa 34 "từ" rộng 17 đơn vị trong một hàng, lý tưởng nhất là chiều rộng tối thiểu là 1156 pixel.

  • Lấy nét hình ảnh kém có thể ảnh hưởng đến độ chính xác của quá trình quét. Nếu bạn không nhận được kết quả 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 cao hơn (chẳng hạn như 1280x720 hoặc 1920x1080), giúp có thể phát hiện được mã vạch từ khoảng cách xa hơn so với máy ảnh.

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

1. Cấu hình máy dò mã vạch

Nếu bạn 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 nó để chỉ phát hiện các định dạng đó.

Ví dụ: để chỉ phát hiện mã Aztec và mã QR, hãy xây dựng đối tượng VisionBarcodeDetectorOptions như trong ví dụ sau:

Nhanh

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

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

  • Mã128
  • Mã39
  • Mã93
  • CodaBar
  • EAN13
  • EAN8
  • ITF
  • UPCA
  • LÊN
  • Mã QR
  • PDF417
  • người Aztec
  • Ma trận dữ liệu

Mục tiêu-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 )
  • Codabar ( FIRVisionBarcodeFormatCodaBar )
  • EAN-13 ( FIRVisionBarcodeFormatEAN13 )
  • EAN-8 ( FIRVisionBarcodeFormatEAN8 )
  • ITF ( FIRVisionBarcodeFormatITF )
  • UPC-A ( FIRVisionBarcodeFormatUPCA )
  • UPC-E ( FIRVisionBarcodeFormatUPCE )
  • Mã QR ( FIRVisionBarcodeFormatQRCode )
  • PDF417 ( FIRVisionBarcodeFormatPDF417 )
  • Aztec ( FIRVisionBarcodeFormatAztec )
  • Ma trận dữ liệu ( FIRVisionBarcodeFormatDataMatrix )

2. Chạy máy dò mã vạch

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

  1. Lấy một phiên bản của VisionBarcodeDetector :

    Nhanh

    lazy var vision = Vision.vision()
    
    let barcodeDetector = vision.barcodeDetector(options: barcodeOptions)
    

    Mục tiêu-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 .

    Để sử dụng UIImage :

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

      Nhanh

      let image = VisionImage(image: uiImage)

      Mục tiêu-C

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

    Để sử dụng CMSampleBufferRef :

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

      Để có được hướng hình ảnh:

      Nhanh

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

      Mục tiêu-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 đó, tạo đối tượng siêu dữ liệu:

      Nhanh

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Mục tiêu-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 một đối tượng VisionImage bằng cách sử dụng đối tượng CMSampleBufferRef và siêu dữ liệu xoay:

      Nhanh

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

      Mục tiêu-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Sau đó, chuyển hình ảnh sang phương thức detect(in:) :

    Nhanh

    barcodeDetector.detect(in: visionImage) { features, error in
      guard error == nil, let features = features, !features.isEmpty else {
        // ...
        return
      }
    
      // ...
    }
    

    Mục tiêu-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 hoạt động 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 được phát hiện trong hình ảnh. Đối với mỗi mã vạch, bạn có thể lấy tọa độ giới hạn của nó trong hình ảnh đầu vào, cũng như dữ liệu thô được mã hóa 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ã hóa bởi mã vạch, bạn có thể nhận được một đối tượng chứa dữ liệu được phân tích cú pháp.

Ví dụ:

Nhanh

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

Mục tiêu-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 thời gian thực

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

  • Không ghi 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ị, việc thu dữ liệu đầu vào ở độ phân giải gốc sẽ tạo ra hình ảnh cực lớn (trên 10 megapixel), dẫn đến độ trễ rất kém và không mang lại lợi ích gì cho độ chính xác. Thay vào đó, chỉ yêu cầu kích thước từ camera cần thiết để phát hiện mã vạch: thường không quá 2 megapixel.

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

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

  • Van tiết lưu gọi tới máy dò. Nếu có khung hình video mới trong khi trình phát hiện đang chạy, hãy thả khung hình đó xuống.
  • Nếu bạn đang sử dụng đầu ra của bộ dò để phủ đồ họa lên hình ảnh đầu vào, trước tiên hãy lấy kết quả từ Bộ công cụ ML, sau đó kết xuất hình ảnh và lớp phủ trong một bước duy nhất. Bằng cách đó, bạn chỉ hiển thị trên bề mặt hiển thị một lần cho mỗi khung hình đầu vào. Xem các lớp PreviewOverlayViewFIRDetectionOverlayView trong ứng dụng mẫu giới thiệu để biết ví dụ.