באמצעות ערכת למידת מכונה, אפשר לזהות ברקודים ולפענח אותם.
רוצה לנסות?
- כדאי לשחק עם האפליקציה לדוגמה כדי לראות שימוש לדוגמה ב-API הזה.
לפני שמתחילים
- יש לכלול את ה-pods הבאים ב-ML Kit ב-Podfile:
pod 'GoogleMLKit/BarcodeScanning', '3.2.0'
- אחרי ההתקנה או העדכון של ה-Pods של הפרויקט, פותחים את פרויקט Xcode באמצעות
.xcworkspace
שלו. ערכת ה-ML נתמכת ב-Xcode מגרסה 12.4 ואילך.
הנחיות להזנת תמונה
-
כדי שערכת ה-ML תקרא ברקודים בצורה מדויקת, תמונות הקלט צריכות להכיל ברקודים שמיוצגים על ידי מספיק נתוני פיקסלים.
הדרישות הספציפיות של נתוני פיקסל תלויות בסוג הברקוד ובכמות הנתונים שמקודדים בו, כי ברקודים רבים תומכים במטען ייעודי (payload) בגודל משתנה. באופן כללי, היחידה המשמעותית ביותר של הברקוד צריכה להיות ברוחב 2 פיקסלים לפחות, ובקוד דו-מימדי בגובה 2 פיקסלים.
לדוגמה, ברקודים מסוג EAN-13 מורכבים מעמודות ומרווחים ברוחב של 1, 2, 3 או 4, כך שתמונה אידיאלית של ברקודים מסוג EAN-13 כוללת ברים ורווחים ברוחב של 2, 4, 6 ו-8 פיקסלים לפחות. מכיוון שברקוד EAN-13 רחב ב-95 יחידות בסך הכול, הברקוד צריך להיות ברוחב של 190 פיקסלים לפחות.
לפורמטים מורכבים יותר, כמו PDF417, נדרשים מימדים גדולים יותר של פיקסלים כדי ש-ML Kit יוכל לקרוא אותם באופן מהימן. לדוגמה, קוד בפורמט PDF417 יכול להכיל עד 34 מילים באורך של עד 34 יחידות, בשורה אידיאלית, ברוחב של לפחות 1,156 פיקסלים.
-
התמקדות גרועה בתמונה יכולה להשפיע על דיוק הסריקה. אם האפליקציה לא מציגה תוצאות מקובלות, צריך לבקש מהמשתמש לצלם אותה מחדש.
-
באפליקציות אופייניות, מומלץ לספק תמונה ברזולוציה גבוהה יותר, כמו למשל 1280x720 או 1920x1080, המאפשרת סריקה של ברקודים ממרחק גדול יותר מהמצלמה.
עם זאת, באפליקציות שבהן זמן האחזור קריטי, אפשר לשפר את הביצועים על ידי צילום תמונות ברזולוציה נמוכה יותר, אבל הברקוד מהווה את רוב תמונת הקלט. כדאי גם לעיין בטיפים לשיפור הביצועים בזמן אמת.
1. הגדרת סורק הברקוד
אם אתם יודעים אילו פורמטים של ברקוד אתם מצפים לקרוא, תוכלו לשפר את המהירות של סורק הברקוד על ידי הגדרה שלו כך שיסרוק רק את הפורמטים האלה.לדוגמה, כדי לסרוק רק קודי Aztec וקודי QR, צריך לבנות אובייקט BarcodeScannerOptions
כמו בדוגמה הבאה:
Swift
let format = .all let barcodeOptions = BarcodeScannerOptions(formats: format)
הפורמטים הבאים נתמכים:
- קוד128
- קוד39
- קוד99
- CodaBar
- dataMatrix
- EAN13
- EAN8
- ITF
- קוד qrCode
- קוד מוצר אוניברסלי (UPCA)
- קוד מוצר אוניברסלי (UPC)
- PDF417
- Aztec
Objective-C
MLKBarcodeScannerOptions *options = [[MLKBarcodeScannerOptions alloc] initWithFormats: MLKBarcodeFormatQRCode | MLKBarcodeFormatAztec];
הפורמטים הבאים נתמכים:
- קוד-128 (
MLKBarcodeFormatCode128
) - קוד-39 (
MLKBarcodeFormatCode39
) - קוד 93 (
MLKBarcodeFormatCode93
) - Codabar (
MLKBarcodeFormatCodaBar
) - מטריצת נתונים (
MLKBarcodeFormatDataMatrix
) - EAN-13 (
MLKBarcodeFormatEAN13
) - EAN-8 (
MLKBarcodeFormatEAN8
) - ITF (
MLKBarcodeFormatITF
) - קוד QR (
MLKBarcodeFormatQRCode
) - קוד מוצר אוניברסלי (UPC-A) (
MLKBarcodeFormatUPCA
) - UPC-E (
MLKBarcodeFormatUPCE
) - PDF-417 (
MLKBarcodeFormatPDF417
) - קוד אצטקי (
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
.אם מהירות הסריקה חשובה, אפשר להנמיך עוד יותר את רזולוציית הצילום. עם זאת, חשוב לזכור את דרישות הגודל המינימליות של הברקוד שתוארו למעלה.
אם מנסים לזהות ברקודים מרצף של פריימים של וידאו בסטרימינג, המזהה עשוי להניב תוצאות שונות בין פריים לפריים. עליך להמתין עד לקבלת סדרה רציפה של אותו ערך כדי להיות בטוח שאתה מחזיר תוצאה טובה.
הספרה Checksum אינה נתמכת עבור ITF וקוד CODE-39.
- לעיבוד מסגרות וידאו, יש להשתמש ב-
results(in:)
ב-API הסינכרוני של המזהה. ניתן לקרוא לשיטה הזו באמצעות הפונקציהAVCaptureVideoDataOutputSampleBufferDelegate
captureOutput(_, didOutput:from:)
כדי לקבל תוצאות סינכרוניות מהפריים של הווידאו. צריך להשאיר את ה-alwaysDiscardsLateVideoFrames
שלAVCaptureVideoDataOutput
בתורtrue
כדי לווסת את השיחות. אם יהפוך לפריים חדש של סרטון בזמן שהמזהה פועל, הוא יוסר. - אם משתמשים בפלט של המזהה כשכבת-על של גרפיקה בתמונת הקלט, קודם צריך לקבל את התוצאה מ-ML Kit, ואז לעבד את התמונה ואת שכבת-העל בפעולה אחת. אם עושים זאת, מתבצע רינדור של התצוגה למשטח התצוגה רק פעם אחת בכל מסגרת קלט שעברה עיבוד. כדי לקבל דוגמה, אפשר לעיין בupdatePreviewLayerViewWithLastFrame שבדוגמה למתחילים של ML.