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

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

ลองเลย

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

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

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

  • เพื่อให้ 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. กำหนดค่าเครื่องสแกนบาร์โค้ด

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

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

Swift

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

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

  • code128
  • code39
  • code93
  • codaBar
  • dataMatrix
  • EAN13
  • EAN8
  • ไอที
  • qrCode
  • รหัส UPCA
  • UPCE
  • PDF417
  • Aztec

Objective-C

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

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

  • โค้ด-128 (MLKBarcodeFormatCode128)
  • Code-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

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

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

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

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