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

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

جرّبه الآن

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

قبل البدء

  1. تضمين مجموعات ML Kit التالية في Podfile:
    pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
    
  2. بعد تثبيت لوحات مشروعك أو تحديثها، افتح مشروع Xcode باستخدام .xcworkspace تتوفّر حزمة تعلّم الآلة في الإصدار 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 في صف واحد، والذي من المفترض أن يكون على الأقل عرض 1156 بكسل

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

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

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

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

التنسيقات التالية متاحة:

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