مسح الرموز الشريطية ضوئيًا باستخدام أدوات تعلّم الآلة على نظام التشغيل iOS

يمكنك استخدام أدوات تعلّم الآلة للتعرّف على الرموز الشريطية وفك ترميزها.

تجربة السمات والبيانات

  • جرِّب نموذج التطبيق للاطّلاع على مثال حول استخدام واجهة برمجة التطبيقات هذه.

قبل البدء

  1. أدرِج مجموعات تعلّم الآلة التالية في Podfile:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. بعد تثبيت أو تحديث مجموعات Pods لمشروعك، افتح مشروع Xcode باستخدام .xcworkspace. تتوفّر هذه الأداة في الإصدار 12.4 من Xcode أو إصدار أحدث.

إرشادات إدخال الصورة

  • لكي تتمكّن أدوات تعلُّم الآلة من قراءة الرموز الشريطية بدقة، يجب أن تتضمّن الصور المدخلة رموزًا شريطية تمثّل بيانات بكسل كافية.

    وتعتمد متطلبات بيانات البكسل المحددة على نوع الرمز الشريطي وكمية البيانات التي يتم ترميزها فيه، لأنّ العديد من الرموز الشريطية تتيح استخدام حمولة ذات حجم متغير. وبشكل عام، يجب ألا يقل عرض الوحدة الأصغر حجمًا للرمز الشريطي عن 2 بكسل، وألا يقل طول الرموز الثنائية الأبعاد عن 2 بكسل.

    على سبيل المثال، تتألف الرموز الشريطية لرقم EAN-13 من أشرطة ومسافات بعرض 1 أو 2 أو 3 أو 4 وحدات، لذا من الأفضل أن تحتوي الصورة الشريطية لرقم EAN-13 على أشرطة ومسافات لا يقلّ عرضها عن 2 و4 و6 و8 بكسل. يجب أن يبلغ عرض الرمز الشريطي EAN-13 95 وحدة بكسل على الأقلّ.

    إنّ التنسيقات الأكثر كثافة، مثل PDF417، تحتاج إلى أبعاد أكبر لوحدات البكسل كي تقرأها بشكل موثوق. على سبيل المثال، يمكن أن يتضمّن الرمز PDF417 ما يصل إلى 34 "كلمة" بعرض 17 وحدة في صف واحد، ويُفضَّل ألّا يقل عرضها عن 1156 بكسل.

  • يمكن أن يؤثّر التركيز السيئ للصورة في دقة المسح الضوئي. إذا لم يحصل تطبيقك على نتائج مقبولة، اطلب من المستخدم إعادة التقاط الصورة.

  • في التطبيقات العادية، يُنصح بتوفير صورة ذات دقة أعلى، مثل 1280×720 أو 1920×1080، ما يجعل الرموز الشريطية قابلة للمسح الضوئي على مسافة أكبر بعيدًا عن الكاميرا.

    ومع ذلك، في التطبيقات التي يكون فيها وقت الاستجابة أمرًا بالغ الأهمية، يمكنك تحسين الأداء من خلال التقاط الصور بدقة أقل، ولكن يجب أن يكون الرمز الشريطي لمعظم الصور التي يتم إدخالها. راجِع أيضًا نصائح لتحسين الأداء في الوقت الفعلي.

1- ضبط الماسح الضوئي للرموز الشريطية

إذا كنت تعرف تنسيقات الرمز الشريطي التي تتوقع قراءتها، يمكنك تحسين سرعة الماسح الضوئي للرموز الشريطية من خلال تكوينه لمسح تلك التنسيقات فقط.

على سبيل المثال، لمسح رمز Aztec ورموز الاستجابة السريعة فقط ضوئيًا، يمكنك إنشاء كائن 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];

تتوفّر التنسيقات التالية:

  • Code-128 (MLKBarcodeFormatCode128)
  • الرمز-39 (MLKBarcodeFormatCode39)
  • Code-93 (MLKBarcodeFormatCode93)
  • كودابار (MLKBarcodeFormatCodaBar)
  • مصفوفة البيانات (MLKBarcodeFormatDataMatrix)
  • رقم EAN-13 (MLKBarcodeFormatEAN13)
  • رقم EAN-8 (MLKBarcodeFormatEAN8)
  • ITF (MLKBarcodeFormatITF)
  • رمز الاستجابة السريعة (MLKBarcodeFormatQRCode)
  • الرمز العالمي للمنتج (UPC-A) (MLKBarcodeFormatUPCA)
  • الرمز العالمي للمنتج (UPC-E) (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • رمز Aztec (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.

  • لمعالجة إطارات الفيديو، استخدِم واجهة برمجة التطبيقات المتزامنة results(in:) لأداة الرصد. ويمكنك استدعاء هذه الطريقة من وظيفة captureOutput(_, didOutput:from:) في AVCaptureVideoDataOutputSampleBufferDelegate للحصول على نتائج متزامنة من إطار الفيديو المحدّد. الاحتفاظ بـ AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames true لتقليل المكالمات الواردة إلى جهاز الرصد. في حال توفّر إطار فيديو جديد أثناء تشغيل أداة الرصد، سيتم تجاهله.
  • إذا كنت تستخدم نتيجة أداة الرصد لإضافة رسومات على الصورة التي تم إدخالها، احصل أولاً على النتيجة من أدوات تعلّم الآلة، ثم اعرض الصورة والتراكب في خطوة واحدة. بإجراء ذلك، يتم عرضك على سطح الشاشة مرة واحدة فقط لكل إطار إدخال تمت معالجته. للاطّلاع على مثال، يمكن الاطّلاع على updatePreviewOverlayViewWithLastFrame في نموذج البدء السريع في ML Kit.