สแกนบาร์โค้ดด้วย ML Kit ใน iOS

คุณใช้ ML Kit เพื่อจดจำและถอดรหัสบาร์โค้ดได้

ลองเลย

ก่อนเริ่มต้น

  1. ใส่พ็อด ML Kit ต่อไปนี้ใน Podfile
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. หลังจากที่คุณติดตั้งหรืออัปเดตพ็อดของโปรเจ็กต์แล้ว ให้เปิดโปรเจ็กต์ Xcode โดยใช้ .xcworkspace ทั้งนี้ ML Kit รองรับ Xcode เวอร์ชัน 12.4 ขึ้นไป

หลักเกณฑ์เกี่ยวกับรูปภาพที่ป้อน

  • เพื่อให้ ML Kit อ่านบาร์โค้ดได้อย่างถูกต้อง รูปภาพที่ป้อนต้องมี บาร์โค้ดที่แสดงด้วยข้อมูลพิกเซลที่เพียงพอ

    สำหรับข้อกำหนดด้านข้อมูลพิกเซลที่เจาะจงจะขึ้นอยู่กับทั้ง กับปริมาณของข้อมูลที่เข้ารหัสไว้ เนื่องจากบาร์โค้ดจำนวนมาก รองรับเพย์โหลดขนาดที่เปลี่ยนแปลงได้ โดยทั่วไป ตัวแปรที่น้อยที่สุด หน่วยบาร์โค้ดควรกว้างอย่างน้อย 2 พิกเซลและ โค้ด 2 มิติ ความสูง 2 พิกเซล

    ตัวอย่างเช่น บาร์โค้ด EAN-13 ประกอบด้วยแท่งและการเว้นวรรคที่ 1 ความกว้าง 2, 3 หรือ 4 หน่วย ดังนั้นรูปภาพบาร์โค้ด EAN-13 จะมีแถบและ ช่องว่างที่มีความกว้างอย่างน้อย 2, 4, 6 และ 8 พิกเซล เนื่องจาก EAN-13 บาร์โค้ดมีความกว้างรวม 95 หน่วย บาร์โค้ดควรมีอย่างน้อย 190 พิกเซล

    รูปแบบที่หนาแน่นกว่า เช่น PDF417 ต้องมีขนาดพิกเซลมากขึ้น ML Kit ให้อ่านได้อย่างน่าเชื่อถือ ตัวอย่างเช่น รหัส PDF417 สามารถมีได้สูงสุด "คำ" กว้าง 17 หน่วย 34 คำ ในแถวเดียว ซึ่งอย่างน้อยก็ กว้าง 1156 พิกเซล

  • การโฟกัสของรูปภาพไม่ดีอาจส่งผลต่อความแม่นยำในการสแกน หากแอปไม่ได้รับ ผลลัพธ์ที่ยอมรับได้ ขอให้ผู้ใช้เรียกคืนรูปภาพอีกครั้ง

  • สำหรับแอปพลิเคชันทั่วไป ขอแนะนำให้เพิ่ม รูปภาพที่มีความละเอียด เช่น 1280x720 หรือ 1920x1080 ซึ่งทำให้บาร์โค้ด สแกนได้ในระยะที่ห่างจากกล้องมากขึ้น

    อย่างไรก็ตาม ในแอปพลิเคชันที่เวลาในการตอบสนองเป็นสิ่งที่สำคัญ คุณสามารถ ด้วยการจับภาพด้วยความละเอียดที่ต่ำลง บาร์โค้ดนั้นทำให้พื้นที่ส่วนใหญ่ของภาพป้อนข้อมูล ดูนี่ด้วย เคล็ดลับในการปรับปรุงประสิทธิภาพแบบเรียลไทม์

1. กำหนดค่าเครื่องสแกนบาร์โค้ด

หากคุณทราบว่าจะอ่านบาร์โค้ดรูปแบบใด คุณสามารถเร่งความเร็ว ของเครื่องสแกนบาร์โค้ดโดยกำหนดค่าให้สแกนเฉพาะรูปแบบเหล่านั้นได้

ตัวอย่างเช่น หากต้องการสแกนเฉพาะโค้ดแอซเท็กและคิวอาร์โค้ด ให้สร้าง BarcodeScannerOptions เช่นเดียวกับใน ตัวอย่างต่อไปนี้

Swift

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

รูปแบบที่รองรับมีดังนี้

  • code128
  • code39
  • code93
  • codaBar
  • dataMatrix
  • EAN13
  • EAN8
  • ITF
  • qrCode
  • UPCA
  • UPCE
  • 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)
  • คิวอาร์โค้ด (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • รหัสแอซเท็ก (MLKBarcodeFormatAztec)

2. เตรียมรูปภาพอินพุต

หากต้องการสแกนบาร์โค้ดในรูปภาพ ให้ส่งรูปภาพเป็น UIImage หรือ CMSampleBufferRef ไปยัง process() หรือ results(in:) ของ BarcodeScanner วิธีการ:

สร้างออบเจ็กต์ VisionImage โดยใช้ UIImage หรือ CMSampleBuffer

หากคุณใช้ UIImage ให้ทำตามขั้นตอนต่อไปนี้

  • สร้างออบเจ็กต์ VisionImage ด้วย UIImage ตรวจสอบว่าได้ระบุ .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;
      }
    }
          
  • สร้างออบเจ็กต์ VisionImage โดยใช้ 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. รับอินสแตนซ์ของ 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;
   }
 }

เคล็ดลับในการปรับปรุงประสิทธิภาพแบบเรียลไทม์

หากต้องการสแกนบาร์โค้ดในแอปพลิเคชันแบบเรียลไทม์ โปรดทำตามขั้นตอนต่อไปนี้ เพื่อให้ได้อัตราเฟรมที่ดีที่สุด

  • อย่าจับภาพอินพุตที่ความละเอียดดั้งเดิมของกล้อง ในอุปกรณ์บางรุ่น การบันทึกอินพุตที่ความละเอียดดั้งเดิมทำให้เกิดขนาดใหญ่มาก (10+ (เมกะพิกเซล) ซึ่งส่งผลให้เวลาในการตอบสนองต่ำมาก โดยที่ไม่เกิดประโยชน์ใดๆ ความแม่นยำ ให้ขอเฉพาะขนาดจากกล้องที่จำเป็นแทน สำหรับการสแกนบาร์โค้ด ซึ่งโดยปกติจะไม่เกิน 2 เมกะพิกเซล

    ค่าที่กำหนดล่วงหน้าของเซสชันการบันทึกที่มีชื่อ - AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow AVCaptureSessionPresetMedium และอื่นๆ) แต่ไม่แนะนำเนื่องจากสามารถจับคู่กับ ความละเอียดที่ไม่เหมาะสมในบางอุปกรณ์ ให้ใช้ค่าที่กำหนดล่วงหน้าแทน เช่น AVCaptureSessionPreset1280x720

    หากความเร็วในการสแกนเป็นสิ่งสำคัญ คุณจะลดการจับภาพลงได้ ความละเอียดสูงสุดของคุณ แต่โปรดคำนึงถึงข้อกำหนดเกี่ยวกับขนาดบาร์โค้ดขั้นต่ำ ตามที่ระบุไว้ข้างต้น

    หากคุณพยายามจำแนกบาร์โค้ดจากลำดับการสตรีม เครื่องมือจดจำอาจสร้างผลการค้นหาที่แตกต่างกันจากเฟรม เฟรม รอจนกว่าจะได้รับซีรีส์เดียวกัน เพื่อให้มั่นใจได้ว่าคุณได้ผลลัพธ์ที่ดี

    ITF และ CODE-39 ไม่รองรับหมายเลข Checksum

  • สำหรับการประมวลผลเฟรมวิดีโอ ให้ใช้ API แบบซิงโครนัสของ results(in:) ในตัวตรวจจับ โทร เมธอดนี้จาก ของ AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:) เพื่อให้ได้ผลลัพธ์จากวิดีโอที่ระบุแบบพร้อมกัน เฟรม เก็บ ของ AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames เป็น true เพื่อควบคุมการโทรหาตัวตรวจจับ หาก เฟรมวิดีโอจะพร้อมใช้งานขณะที่ตัวตรวจจับทำงานอยู่ เฟรมนั้นจะหายไป
  • หากคุณใช้เอาต์พุตของเครื่องมือตรวจจับเพื่อวางซ้อนกราฟิก รูปภาพอินพุต รับผลลัพธ์จาก ML Kit ก่อน จากนั้นจึงแสดงผลรูปภาพ ซ้อนทับในขั้นตอนเดียว การทำเช่นนี้จะช่วยให้แสดงผลบนพื้นผิวจอแสดงผล เพียงครั้งเดียวสำหรับแต่ละเฟรมอินพุตที่ประมวลผลแล้ว ดู updatePreviewOverlayViewWithLastFrame ในตัวอย่างการเริ่มต้นอย่างรวดเร็วใน ML Kit