סריקת ברקודים באמצעות ערכת ML ב-iOS

אתם יכולים להשתמש ב-ML Kit כדי לזהות ולפענח קודי מ barras.

לפני שמתחילים

  1. אם עדיין לא הוספתם את Firebase לאפליקציה, צריך לבצע את הפעולות הבאות במדריך לתחילת העבודה.
  2. כוללים את ספריות ML Kit ב-Podfile:
    pod 'Firebase/MLVision'
    pod 'Firebase/MLVisionBarcodeModel'
    
    אחרי שמתקינים או מעדכנים את קבוצות ה-Pod של הפרויקט, חשוב לפתוח את ה-Xcode באמצעות ה-.xcworkspace שלו.
  3. מייבאים את Firebase לאפליקציה:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

הנחיות להוספת תמונה

  • כדי ש-ML Kit יוכל לקרוא ברקודים באופן מדויק, תמונות הקלט חייבות להכיל ברקודים שמיוצגים על ידי כמות מספקת של נתוני פיקסלים.

    הדרישות הספציפיות לנתוני הפיקסלים תלויות גם בסוג הברקוד וגם בכמות הנתונים שמקודדים בו (כי רוב הברקודים תומכים בתוכן טעון באורך משתנה). באופן כללי, המשמעות יחידת הברקוד צריכה להיות ברוחב 2 פיקסלים לפחות קודים דו-ממדיים, בגובה של 2 פיקסלים).

    לדוגמה, ברקודים מסוג EAN-13 מורכבים מעמודות ומרווחים שהם 1, רוחב של 2, 3 או 4 יחידות, כך שתמונת ברקוד מסוג EAN-13 באופן אידיאלי כוללת פסים רווחים ברוחב 2, 4, 6 ו-8 פיקסלים לפחות. מאחר שתקן EAN-13 הברקוד הוא ברוחב 95 יחידות. הוא צריך להיות לפחות 190 יחידות פיקסלים לרוחב.

    בפורמטים צפופים יותר, כמו PDF417, נדרשים מידות פיקסלים גבוהות יותר ML Kit כדי לקרוא אותם בצורה אמינה. לדוגמה, קוד PDF417 יכול לכלול עד 34 "מילים" ברוחב 17 יחידות בשורה אחת, שרוחב הרצוי שלה הוא לפחות 1,156 פיקסלים.

  • מיקוד תמונה לא טוב עלול לפגוע בדיוק הסריקה. אם התוצאות לא מתקבלות, נסו לבקש מהמשתמש לצלם מחדש את התמונה.

  • באפליקציות טיפוסיות מומלץ לספק ערך גבוה יותר תמונה ברזולוציה גבוהה (למשל 1280x720 או 1920x1080), שמפיקה ברקודים. במרחק גדול יותר מהמצלמה.

    עם זאת, באפליקציות שבהן זמן האחזור קריטי, ניתן לשפר צילום תמונות ברזולוציה נמוכה יותר, אבל נדרש הברקוד מהווה את רוב תמונת הקלט. מומלץ לקרוא גם את המאמר טיפים לשיפור הביצועים בזמן אמת.

1. הגדרת גלאי הברקוד

אם ידוע לך אילו פורמטים של ברקוד היית רוצה לקרוא, אפשר להאיץ את התהליך של מזהה הברקוד על ידי הגדרתו כך שיזהה רק את הפורמטים האלה.

לדוגמה, כדי לזהות רק קוד אצטקי וקודי QR, באובייקט VisionBarcodeDetectorOptions, כמו בדוגמה הבאה:

Swift

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

הפורמטים הבאים נתמכים:

  • Code128
  • קוד39
  • Code93
  • CodaBar
  • מספר EAN13
  • EAN8
  • ITF
  • UPCA
  • UPCE
  • קוד QR
  • PDF417
  • אצטקים
  • DataMatrix

Objective-C

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

הפורמטים הבאים נתמכים:

  • קוד 128 (FIRVisionBarcodeFormatCode128)
  • קוד 39 (FIRVisionBarcodeFormatCode39)
  • קוד 93 (FIRVisionBarcodeFormatCode93)
  • Codabar (FIRVisionBarcodeFormatCodaBar)
  • EAN-13 (FIRVisionBarcodeFormatEAN13)
  • EAN-8 (FIRVisionBarcodeFormatEAN8)
  • ITF (FIRVisionBarcodeFormatITF)
  • UPC-A (FIRVisionBarcodeFormatUPCA)
  • UPC-E (FIRVisionBarcodeFormatUPCE)
  • קוד QR (FIRVisionBarcodeFormatQRCode)
  • PDF417 (FIRVisionBarcodeFormatPDF417)
  • אצטקית (FIRVisionBarcodeFormatAztec)
  • מטריצת נתונים (FIRVisionBarcodeFormatDataMatrix)

2. הפעלת גלאי הברקוד

כדי לסרוק ברקודים בתמונה, מעבירים את התמונה כ-UIImage או CMSampleBufferRef ל-detect(in:) של VisionBarcodeDetector method:

  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 מגה-פיקסלים), וכתוצאה מכך זמן האחזור (latency) נמוך מאוד ללא שיפור ברמת הדיוק. במקום זאת, צריך לבקש מהמצלמה רק את הגודל שנחוץ לזיהוי ברקוד: בדרך כלל לא יותר מ-2 מגה-פיקסלים.

    ההגדרות הקבועות מראש לסשן הצילום שקיבלו שם – AVCaptureSessionPresetDefault, AVCaptureSessionPresetLow, AVCaptureSessionPresetMedium וכן הלאה) – לא מומלץ, כי הם יכולים למפות ורזולוציות לא מתאימות במכשירים מסוימים. במקום זאת, משתמשים בהגדרות הקבועות מראש הספציפיות כמו AVCaptureSessionPreset1280x720.

    אם מהירות הסריקה חשובה, אפשר להאט עוד יותר את צילום התמונה ורזולוציה. עם זאת, חשוב לזכור את הדרישות המינימליות לגודל הברקוד שתוארו למעלה.

  • צמצום מספר הקריאות למזהה. אם פריים חדש בסרטון הופך בזמן שהגלאי פועל, משחררים את הפריים.
  • אם משתמשים בפלט של הגלאי כדי להציג גרפיקה בשכבת-על מקבלים קודם את התוצאה מ-ML Kit ואז מעבדים את התמונה וליצור שכבת-על בשלב אחד. כך תוכלו להציג את משטח המסך פעם אחת בלבד לכל מסגרת קלט. אפשר לעיין ב-previewOverlayView ו-FIRDetectionOverlayView באפליקציה לדוגמה של Showcase.