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

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

قبل ان تبدأ

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

    سويفت

    import Firebase

    ج موضوعية

    @import Firebase;

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

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

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

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

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

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

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

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

1. قم بتكوين كاشف الباركود

إذا كنت تعرف تنسيقات الباركود التي تتوقع قراءتها، فيمكنك تحسين سرعة كاشف الباركود عن طريق تكوينه لاكتشاف تلك التنسيقات فقط.

على سبيل المثال، لاكتشاف رمز Aztec ورموز QR فقط، قم بإنشاء كائن VisionBarcodeDetectorOptions كما في المثال التالي:

سويفت

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

يتم دعم التنسيقات التالية:

  • كود128
  • كود39
  • كود93
  • كودابار
  • EAN13
  • EAN8
  • الـITF
  • UPCA
  • UPCE
  • رمز الاستجابة السريعة
  • PDF417
  • الأزتيك
  • مصفوفة البيانات

ج موضوعية

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 :

    سويفت

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

    ج موضوعية

    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 .

      سويفت

      let image = VisionImage(image: uiImage)

      ج موضوعية

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

    لاستخدام CMSampleBufferRef :

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

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

      سويفت

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

      ج موضوعية

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

      ثم قم بإنشاء كائن بيانات التعريف:

      سويفت

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

      ج موضوعية

      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 وبيانات تعريف التدوير:

      سويفت

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

      ج موضوعية

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. ثم قم بتمرير الصورة إلى طريقة detect(in:) :

    سويفت

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

    ج موضوعية

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

3. الحصول على المعلومات من الباركود

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

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

سويفت

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

ج موضوعية

 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 في نموذج تطبيق العرض للحصول على مثال.