اسکن کردن بارکدها با ML Kit در iOS

می‌توانید از ML Kit برای تشخیص و رمزگشایی بارکدها استفاده کنید.

امتحان کنید

  • با برنامه نمونه بازی کنید تا نمونه‌ای از کاربرد این API را ببینید.

قبل‌از شروع

  1. غلاف‌های ML Kit زیر را در Podfile خود بگنجانید:
    pod 'GoogleMLKit/BarcodeScanning', '8.0.0'
    
  2. پس‌از نصب یا به‌روزرسانی Pods پروژه، پروژه Xcode خود را بااستفاده از .xcworkspace باز کنید. ‫ML Kit در Xcode نسخه ۱۲.۴ یا بالاتر پشتیبانی می‌شود.

دستورالعمل‌های تصویر ورودی

  • برای اینکه ML Kit بارکدها را به‌دقت بخواند، تصاویر ورودی باید حاوی بارکدهایی باشند که با داده‌های پیکسل کافی نشان داده شده‌اند.

    الزامات خاص داده‌های پیکسل هم به نوع بارکد و هم به مقدار داده‌هایی که در آن کدگذاری شده است بستگی دارد، زیرا بسیاری از بارکدها از اندازه متغیر بار مفید پشتیبانی می‌کنند. به‌طورکلی، کوچک‌ترین واحد معنادار بارکد باید حداقل ۲ پیکسل عرض داشته باشد، و برای کدهای ۲ بعدی، ۲ پیکسل ارتفاع داشته باشد.

    برای مثال، بارکدهای EAN-13 از خطوط و فاصله‌هایی تشکیل شده‌اند که ۱، ۲، ۳، یا ۴ واحد پهنا دارند، بنابراین تصویر بارکد EAN-13 به‌طور ایده‌آل دارای خطوط و فاصله‌هایی است که حداقل ۲، ۴، ۶، و ۸ پیکسل پهنا دارند. چون بارکد EAN-13 در مجموع ۹۵ واحد پهنا دارد، بارکد باید حداقل ۱۹۰ پیکسل پهنا داشته باشد.

    قالب‌های متراکم‌تر، مانند PDF417، برای اینکه ML Kit بتواند آن‌ها را به‌طور قابل‌اعتمادی بخواند، به ابعاد پیکسلی بزرگ‌تری نیاز دارند. برای مثال، یک کد PDF417 می‌تواند حداکثر ۳۴ «کلمه» با عرض ۱۷ واحد در یک ردیف داشته باشد که در حالت ایده‌آل باید حداقل ۱۱۵۶ پیکسل عرض داشته باشد.

  • فوکوس ضعیف تصویر می‌تواند بر دقت اسکن تأثیر بگذارد. اگر برنامه‌تان نتایج قابل‌قبولی دریافت نمی‌کند، از کاربر بخواهید تصویر را دوباره ضبط کند.

  • برای برنامه‌های معمولی، توصیه می‌شود تصویری با وضوح بالاتر ارائه شود، مثلاً ۱۲۸۰×۷۲۰ یا ۱۹۲۰×۱۰۸۰، که باعث می‌شود رمزینه‌ها از فاصله دورتری از دوربین قابل‌اسکن باشند.

    بااین‌حال، در برنامه‌هایی که تأخیر در آن‌ها حیاتی است، می‌توانید با گرفتن تصویر در وضوح پایین‌تر، عملکرد را بهبود دهید، اما باید بارکد بخش عمده‌ای از تصویر ورودی را تشکیل دهد. همچنین نکاتی برای بهبود عملکرد هم‌زمان را ببینید.

۱. پیکربندی اسکنر بارکد

اگر می‌دانید که انتظار دارید کدام قالب‌های بارکد خوانده شود، می‌توانید با پیکربندی اسکنر بارکد برای اسکن کردن فقط آن قالب‌ها، سرعت آن را بهبود دهید.

برای مثال، برای اسکن کردن فقط کد Aztec و رمزینه‌های پاسخ‌سریع، شیء BarcodeScannerOptions را مانند مثال زیر بسازید:

Swift

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

قالب‌های زیر پشتیبانی می‌شوند:

  • code128
  • code39
  • code93
  • codaBar
  • dataMatrix
  • EAN13
  • EAN8
  • ITF
  • qrCode
  • UPCA
  • UPCE
  • PDF417
  • آزتک

آبجکتیو-سی

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

قالب‌های زیر پشتیبانی می‌شوند:

  • کد-۱۲۸ (MLKBarcodeFormatCode128)
  • Code-39 (MLKBarcodeFormatCode39)
  • Code-93 (MLKBarcodeFormatCode93)
  • Codabar (MLKBarcodeFormatCodaBar)
  • ماتریکس داده‌ها (MLKBarcodeFormatDataMatrix)
  • EAN-13‏ (MLKBarcodeFormatEAN13)
  • EAN-8 (MLKBarcodeFormatEAN8)
  • ITF (MLKBarcodeFormatITF)
  • رمزینه پاسخ‌سریع (MLKBarcodeFormatQRCode)
  • UPC-A (MLKBarcodeFormatUPCA)
  • UPC-E (MLKBarcodeFormatUPCE)
  • PDF-417‏ (MLKBarcodeFormatPDF417)
  • کد آزتک (MLKBarcodeFormatAztec)

۲. آماده کردن تصویر ورودی

برای اسکن کردن رمزینه‌ها در تصویر، تصویر را به‌عنوان UIImage یا CMSampleBufferRef به روش process() یا results(in:) BarcodeScanner ارسال کنید:

بااستفاده از UIImage یا CMSampleBuffer، یک شیء VisionImage ایجاد کنید.

اگر از UIImage استفاده می‌کنید، این مراحل را دنبال کنید:

  • با UIImage، شیء VisionImage ایجاد کنید. حتماً .orientation صحیح را مشخص کنید.

    Swift

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

    آبجکتیو-سی

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

    آبجکتیو-سی

    - (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;
      }
    }
          
  • بااستفاده از شیء CMSampleBuffer و جهت‌گیری، شیء VisionImage ایجاد کنید:

    Swift

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    آبجکتیو-سی

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

۳. دریافت نمونه‌ای از BarcodeScanner

نمونه‌ای از BarcodeScanner را دریافت کنید:

Swift

let barcodeScanner = BarcodeScanner.barcodeScanner()
// Or, to change the default settings:
// let barcodeScanner = BarcodeScanner.barcodeScanner(options: barcodeOptions)

آبجکتیو-سی

MLKBarcodeScanner *barcodeScanner = [MLKBarcodeScanner barcodeScanner];
// Or, to change the default settings:
// MLKBarcodeScanner *barcodeScanner =
//     [MLKBarcodeScanner barcodeScannerWithOptions:options];

۴. پردازش تصویر

سپس تصویر را به روش process() ارسال کنید:

Swift

barcodeScanner.process(visionImage) { features, error in
  guard error == nil, let features = features, !features.isEmpty else {
    // Error handling
    return
  }
  // Recognized barcodes
}

آبجکتیو-سی

[barcodeScanner processImage:image
                  completion:^(NSArray<MLKBarcode *> *_Nullable barcodes,
                               NSError *_Nullable error) {
  if (error != nil) {
    // Error handling
    return;
  }
  if (barcodes.count > 0) {
    // Recognized barcodes
  }
}];

۵. دریافت اطلاعات از رمزینه‌ها

اگر عملیات اسکن بارکد موفقیت‌آمیز باشد، اسکنر آرایه‌ای از 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
  }
}

آبجکتیو-سی

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

نکته‌هایی برای بهبود عملکرد هم‌زمان

اگر می‌خواهید بارکدها را در یک برنامه درلحظه اسکن کنید، این دستورالعمل‌ها را دنبال کنید تا به بهترین نرخ‌های فریم دست یابید:

  • ورودی را با وضوح اصلی دوربین ضبط نکنید. در برخی‌از دستگاه‌ها، ضبط ورودی با وضوح بومی منجر به تولید تصاویر بسیار بزرگ (بیش‌از ۱۰ مگاپیکسل) می‌شود که درنتیجه تأخیر بسیار بدی بدون هیچ‌گونه سودی برای دقت ایجاد می‌کند. درعوض، فقط اندازه موردنیاز برای اسکن کردن بارکد را از دوربین درخواست کنید که معمولاً بیشتر از ۲ مگاپیکسل نیست.

    بااین‌حال، پیش‌تنظیم‌های جلسه ضبط نام‌گذاری‌شده (AVCaptureSessionPresetDefault، AVCaptureSessionPresetLow، AVCaptureSessionPresetMedium، و غیره) توصیه نمی‌شوند، زیرا ممکن است در برخی دستگاه‌ها به وضوح‌های نامناسب نگاشت شوند. به‌جای آن، از پیش‌تنظیم‌های خاص مثل AVCaptureSessionPreset1280x720 استفاده کنید.

    اگر سرعت اسکن کردن برایتان مهم است، می‌توانید وضوح تصویربرداری را بیشتر کاهش دهید. بااین‌حال، الزامات حداقل اندازه بارکد را که در بالا ذکر شده است به‌خاطر داشته باشید.

    اگر می‌خواهید بارکدها را از توالی قاب‌های ویدیو جاری‌سازی تشخیص دهید، ممکن است تشخیص‌دهنده نتایج متفاوتی از قاب به قاب تولید کند. باید صبر کنید تا مجموعه‌ای پیوسته از مقدار یکسان دریافت کنید تا مطمئن شوید نتیجه خوبی برمی‌گردانید.

    رقم «جمع‌آزما» برای ITF و CODE-39 پشتیبانی نمی‌شود.

  • برای پردازش قاب‌های ویدیو، از results(in:) میانای برنامه‌سازی کاربردی هم‌زمان آشکارساز استفاده کنید. این روش را از تابع captureOutput(_, didOutput:from:) مربوط به AVCaptureVideoDataOutputSampleBufferDelegate فراخوانی کنید تا نتایج را به‌صورت همگام از فریم ویدیو داده‌شده دریافت کنید. AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames را به‌عنوان true نگه دارید تا تماس‌های برقرارشده با آشکارساز را محدود کنید. اگر قاب ویدیو جدیدی درحین اجرای آشکارساز دردسترس قرار گیرد، حذف خواهد شد.
  • اگر از برونداد آشکارساز برای رونهادن گرافیک روی تصویر ورودی استفاده می‌کنید، ابتدا نتیجه را از ML Kit دریافت کنید، سپس تصویر را پرداز و در یک مرحله رونهاد کنید. با انجام این کار، برای هر قاب ورودی پردازش‌شده، فقط یک‌بار به سطح نمایشگر رندر می‌کنید. برای نمونه، updatePreviewOverlayViewWithLastFrame را در نمونه راه‌اندازی سریع ML Kit ببینید.