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

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

آن را امتحان کنید

قبل از شروع

  1. پادهای کیت ML زیر را در فایل پادفایل خود قرار دهید:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. پس از نصب یا به روز رسانی Pods پروژه خود، پروژه Xcode خود را با استفاده از .xcworkspace . آن باز کنید. کیت ML در Xcode نسخه 12.4 یا بالاتر پشتیبانی می شود.

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

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

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

    برای مثال، بارکدهای EAN-13 از میله‌ها و فضاهایی با عرض 1، 2، 3 یا 4 واحد تشکیل شده‌اند، بنابراین تصویر بارکد EAN-13 در حالت ایده‌آل دارای نوارها و فضاهایی است که حداقل 2، 4، 6 و عرض 8 پیکسل از آنجایی که یک بارکد EAN-13 در مجموع 95 واحد عرض دارد، بارکد باید حداقل 190 پیکسل باشد.

    فرمت‌های متراکم‌تر، مانند PDF417، برای خواندن قابل اطمینان ML Kit به ابعاد پیکسل بیشتری نیاز دارند. به عنوان مثال، یک کد PDF417 می تواند حداکثر 34 کلمه 17 واحدی در یک ردیف داشته باشد که در حالت ایده آل حداقل 1156 پیکسل عرض دارد.

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

  • برای برنامه‌های معمولی، ارائه تصویری با وضوح بالاتر، مانند 1280x720 یا 1920x1080 توصیه می‌شود که بارکدها را از فاصله‌ای دورتر از دوربین قابل اسکن می‌کند.

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

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

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

برای مثال، برای اسکن فقط کدهای آزتک و کدهای QR، یک شیء BarcodeScannerOptions را مانند مثال زیر بسازید:

سویفت

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

فرمت های زیر پشتیبانی می شوند:

  • کد 128
  • کد39
  • کد93
  • codaBar
  • dataMatrix
  • EAN13
  • EAN8
  • ITF
  • qrCode
  • UPCA
  • UPCE
  • PDF417
  • آزتک

هدف-C

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

فرمت های زیر پشتیبانی می شوند:

  • Code-128 ( MLKBarcodeFormatCode128 )
  • Code-39 ( MLKBarcodeFormatCode39 )
  • Code-93 ( MLKBarcodeFormatCode93 )
  • کدابار ( MLKBarcodeFormatCodaBar )
  • ماتریس داده ( MLKBarcodeFormatDataMatrix )
  • EAN-13 ( MLKBarcodeFormatEAN13 )
  • EAN-8 ( MLKBarcodeFormatEAN8 )
  • ITF ( MLKBarcodeFormatITF )
  • کد QR ( MLKBarcodeFormatQRCode )
  • UPC-A ( MLKBarcodeFormatUPCA )
  • UPC-E ( MLKBarcodeFormatUPCE )
  • PDF-417 ( MLKBarcodeFormatPDF417 )
  • کد آزتک ( MLKBarcodeFormatAztec )

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

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

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

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

  • با UIImage یک شی VisionImage ایجاد کنید. مطمئن شوید که جهت .orientation را مشخص کرده اید.

    سویفت

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

    هدف-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

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

  • جهت داده های تصویر موجود در CMSampleBuffer را مشخص کنید.

    برای دریافت جهت تصویر:

    سویفت

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

    هدف-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 و جهت گیری ایجاد کنید:

    سویفت

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

    هدف-C

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

3. یک نمونه از BarcodeScanner را دریافت کنید

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

سویفت

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

هدف-C

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

4. تصویر را پردازش کنید

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

سویفت

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

هدف-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 نشان دهنده بارکدی است که در تصویر شناسایی شده است. برای هر بارکد، می توانید مختصات مرزی آن را در تصویر ورودی و همچنین داده های خام کدگذاری شده توسط بارکد را دریافت کنید. همچنین، اگر اسکنر بارکد قادر به تعیین نوع داده های کدگذاری شده توسط بارکد بود، می توانید یک شی حاوی داده های تجزیه شده را دریافت کنید.

به عنوان مثال:

سویفت

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

هدف-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 فراخوانی کنید تا به طور همزمان نتایج را از فریم ویدیوی داده شده دریافت کنید. قاب‌های AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames DiscardsLateVideoFrames را برای کاهش تماس‌های آشکارساز true نگه دارید. اگر یک قاب ویدیویی جدید در حالی که آشکارساز در حال کار است در دسترس باشد، حذف خواهد شد.
  • اگر از خروجی آشکارساز برای همپوشانی گرافیک روی تصویر ورودی استفاده می‌کنید، ابتدا نتیجه را از کیت ML بگیرید، سپس تصویر را در یک مرحله رندر کنید و همپوشانی کنید. با انجام این کار، برای هر فریم ورودی پردازش شده فقط یک بار به سطح نمایشگر رندر می دهید. به عنوان مثال به updatePreviewOverlayViewWithLastFrame در نمونه راه اندازی سریع ML Kit مراجعه کنید.