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

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.

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

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

قبل البدء

  1. يمكنك تضمين لوحات ML Kit في ملف Podfile:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. بعد تثبيت المشروع أو تعديلها، افتح مشروع Xcode باستخدام .xcworkspace. حزمة ML Kit متوافقة مع الإصدار 12.4 من Xcode أو الإصدارات الأحدث.

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

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

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

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

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

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

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

    ويمكنك تحسين الأداء في التطبيقات من خلال التقاط الصور بدقة أقل، ولكن مع طلب رمز شريطي يمثّل معظم الصورة المستخدَمة. اطّلِع أيضًا على نصائح لتحسين الأداء في الوقت الفعلي.

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

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

على سبيل المثال، لفحص رمز Aztec ورموز الاستجابة السريعة فقط، يمكنك إنشاء كائن BarcodeScannerOptions كما في المثال التالي:

Swift

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

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

  • الرمز 128
  • الرمز 39
  • الرمز 93
  • كودابار
  • مصفوفة البيانات
  • EAN13
  • EAN8
  • إطار العمل الدولي
  • رمز الاستجابة السريعة
  • الرمز العالمي للمنتج (UPC)
  • الرمز العالمي للمنتج (UPC)
  • 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) (MLKBarcodeFormatUPCA)
  • الرمز العالمي للمنتج (UPC) (MLKBarcodeFormatUPCE)
  • PDF-417 (MLKBarcodeFormatPDF417)
  • رمز Aztec (MLKBarcodeFormatAztec)

2. إعداد صورة الإدخال

لمسح الرموز الشريطية ضوئيًا في صورة، يتم تمرير الصورة كطريقة UIImage أو CMSampleBufferRef إلى BarcodeScanner's process() أو results(in:) طريقة:

يمكنك إنشاء كائن 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:) المتزامنة لأداة الرصد. عليك استدعاء هذه الطريقة من الدالة AVCaptureVideoDataOutputSampleBufferDelegate's captureOutput(_, didOutput:from:) للحصول على النتائج بشكل متزامن من إطار الفيديو المحدّد. ابقَ على AVCaptureVideoDataOutput&36 alwaysDiscardsLateVideoFrames كـ true للتحكُّم في المكالمات إلى أداة الرصد. سيتم تجاهل إطار فيديو جديد أثناء تشغيل أداة الرصد.
  • إذا كنت تستخدم ناتج أداة الكشف لتراكب الرسومات على الصورة المُدخلة، عليك أولاً الحصول على النتيجة من حزمة تعلّم الآلة، ثم عرض الصورة والتراكب على خطوة واحدة. يؤدي إجراء ذلك إلى عرض لوحة البيانات على الشاشة مرة واحدة فقط لكل إطار إدخال تمت معالجته. للاطّلاع على مثال، يمكنك الاطّلاع على updatepreviewتراكبViewWithLastframe في نموذج البدء السريع لتطبيق ML Kit.