مسح الرموز الشريطية ضوئيًا باستخدام ML Kit على أجهزة iOS

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

قبل البدء

  1. إذا لم يسبق لك إضافة Firebase إلى تطبيقك، يمكنك إجراء ذلك من خلال اتّباع الخطوات الأولى في دليل البدء.
  2. تضمين مكتبات ML Kit في Podfile:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    بعد تثبيت مجموعات مشروعك الصغيرة أو تحديثها، احرص على فتح ملف Xcode باستخدام .xcworkspace.
  3. في تطبيقك، استورد Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

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

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

    تعتمد المتطلبات المحددة لبيانات البكسل على كل من نوع الرمز الشريطي ومقدار البيانات المشفرة فيه (بما أن معظم الرموز الشريطية) تدعم حمولة متغيرة الطول). وبشكل عام، تُعد أصغر معنى يجب ألا يقل عرض وحدة الرمز الشريطي عن 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 ورموز الاستجابة السريعة فقط، يمكنك إنشاء VisionBarcodeDetectorOptions كما في المثال التالي:

Swift

let format = VisionBarcodeFormat.all
let barcodeOptions = VisionBarcodeDetectorOptions(formats: format)

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

  • الرمز 128
  • الرمز 39
  • الرمز 93
  • كودا بار
  • رقم EAN13
  • رقم EAN8
  • فرقة ITF
  • قانون الألفية الجديدة لحقوق طبع ونشر المواد الرقمية (UPCA)
  • اختبار UPCE
  • رمز الاستجابة السريعة
  • PDF417
  • أزتكي
  • مصفوفة البيانات

Objective-C

FIRVisionBarcodeDetectorOptions *options =
    [[FIRVisionBarcodeDetectorOptions alloc]
     initWithFormats: FIRVisionBarcodeFormatQRCode | FIRVisionBarcodeFormatAztec];

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

  • الرمز 128 (FIRVisionBarcodeFormatCode128)
  • الرمز 39 (FIRVisionBarcodeFormatCode39)
  • الرمز 93 (FIRVisionBarcodeFormatCode93)
  • الكودابار (FIRVisionBarcodeFormatCodaBar)
  • رقم EAN-13 (FIRVisionBarcodeFormatEAN13)
  • رقم EAN-8 (FIRVisionBarcodeFormatEAN8)
  • ITF (FIRVisionBarcodeFormatITF)
  • الرمز العالمي للمنتج (UPC)-A (FIRVisionBarcodeFormatUPCA)
  • الرمز العالمي للمنتج (UPC)-E (FIRVisionBarcodeFormatUPCE)
  • رمز الاستجابة السريعة (FIRVisionBarcodeFormatQRCode)
  • PDF417 (FIRVisionBarcodeFormatPDF417)
  • أزتيك (FIRVisionBarcodeFormatAztec)
  • مصفوفة البيانات (FIRVisionBarcodeFormatDataMatrix)

2- تشغيل أداة رصد الرموز الشريطية

لمسح الرموز الشريطية ضوئيًا في صورة، مرِّر الصورة على أنّها UIImage أو من CMSampleBufferRef إلى detect(in:) في VisionBarcodeDetector :

  1. الحصول على مثال VisionBarcodeDetector:

    Swift

    lazy var vision = Vision.vision()
    
    let barcodeDetector = vision.barcodeDetector(options: barcodeOptions)
    

    Objective-C

    FIRVision *vision = [FIRVision vision];
    FIRVisionBarcodeDetector *barcodeDetector = [vision barcodeDetector];
    // Or, to change the default settings:
    // FIRVisionBarcodeDetector *barcodeDetector =
    //     [vision barcodeDetectorWithOptions:options];
    
  2. إنشاء عنصر VisionImage باستخدام UIImage أو CMSampleBufferRef

    لاستخدام UIImage:

    1. إذا لزم الأمر، يمكنك تدوير الصورة لتكون imageOrientation الموقع هو .up.
    2. إنشاء عنصر VisionImage باستخدام عنصر التدوير الذي تم تدويره بشكل صحيح UIImage عدم تحديد أي بيانات وصفية حول عرض الإعلانات بالتناوب - البيانات التلقائية القيمة، .topLeft،.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    لاستخدام CMSampleBufferRef:

    1. أنشئ كائن VisionImageMetadata يحدّد اتجاه بيانات الصورة الواردة في المخزن المؤقت CMSampleBufferRef

      للحصول على اتجاه الصورة:

      Swift

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Objective-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      بعد ذلك، أنشئ كائن البيانات الوصفية:

      Swift

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Objective-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. إنشاء عنصر VisionImage باستخدام عنصر CMSampleBufferRef والبيانات الوصفية بالتناوب:

      Swift

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. بعد ذلك، اضبط الصورة على طريقة detect(in:):

    Swift

    barcodeDetector.detect(in: visionImage) { features, error in
      guard error == nil, let features = features, !features.isEmpty else {
        // ...
        return
      }
    
      // ...
    }
    

    Objective-C

    [barcodeDetector detectInImage:image
                        completion:^(NSArray<FIRVisionBarcode *> *barcodes,
                                     NSError *error) {
      if (error != nil) {
        return;
      } else if (barcodes != nil) {
        // Recognized barcodes
        // ...
      }
    }];
    

3- الحصول على معلومات من الرموز الشريطية

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

على سبيل المثال:

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 (FIRVisionBarcode *barcode in barcodes) {
   NSArray *corners = barcode.cornerPoints;

   NSString *displayValue = barcode.displayValue;
   NSString *rawValue = barcode.rawValue;

   FIRVisionBarcodeValueType valueType = barcode.valueType;
   switch (valueType) {
     case FIRVisionBarcodeValueTypeWiFi:
       // ssid = barcode.wifi.ssid;
       // password = barcode.wifi.password;
       // encryptionType = barcode.wifi.type;
       break;
     case FIRVisionBarcodeValueTypeURL:
       // url = barcode.URL.url;
       // title = barcode.URL.title;
       break;
     // ...
     default:
       break;
   }
 }

نصائح لتحسين الأداء في الوقت الفعلي

إذا أردت مسح الرموز الشريطية ضوئيًا في تطبيق في الوقت الفعلي، اتّبِع الخطوات التالية: الإرشادات لتحقيق أفضل معدلات عرض الإطارات:

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

    الإعدادات المسبقة لجلسة التسجيل المُسمّاة: AVCaptureSessionPresetDefault AVCaptureSessionPresetLow، AVCaptureSessionPresetMedium، وما إلى ذلك) - لا يوصى بها، حيث يمكنهم التعيين إلى ودرجات الدقة غير المناسبة على بعض الأجهزة. بدلاً من ذلك، استخدم الإعدادات المسبقة المحددة مثل AVCaptureSessionPreset1280x720.

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

  • تقييد المكالمات الواردة إلى أداة الرصد. إذا أصبح إطار فيديو جديد المتاح أثناء تشغيل أداة الكشف، أفلِت الإطار.
  • إذا كنت تستخدم ناتج أداة الكشف لتراكب الرسومات على الصورة المدخلة، والحصول أولاً على النتيجة من ML Kit، ثم عرض الصورة وتراكبها في خطوة واحدة. ومن خلال القيام بذلك، يمكنك العرض على سطح الشاشة مرة واحدة فقط لكل إطار إدخال اطّلع على previewOverlayView وFIRDetectionOverlayView الفئات في نموذج تطبيق العرض كمثال.