זיהוי טקסט בתמונות באמצעות ML Kit ב-iOS

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

ממשק API לזיהוי טקסט גרסה 2
תיאורקל לזהות טקסט בתמונות או בסרטונים, תמיכה בסקריפטים לטיניים, סיניים, דוונאגרי, יפנית וקוריאנית, ומגוון רחב של שפות.
שמות SDKGoogleMLKit/TextRecognition
GoogleMLKit/TextRecognitionChinese
GoogleMLKit/TextRecognitionDevanagari
GoogleMLKit/TextRecognitionJapanese
GoogleMLKit/TextRecognitionKorean
יישוםהנכסים מקושרים באופן סטטי לאפליקציה שלך בזמן היצירה
ההשפעה על גודל האפליקציהכ-38MB לכל סקריפט של סקריפט
ביצועיםזמן אמת ברוב המכשירים ל-SDK לסקריפט לטיני, איטי יותר לאחרים.

אני רוצה לנסות

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

  1. מוסיפים את רכיבי ה-ML Kit ל-Podfile:
    # To recognize Latin script
    pod 'GoogleMLKit/TextRecognition', '3.2.0'
    # To recognize Chinese script
    pod 'GoogleMLKit/TextRecognitionChinese', '3.2.0'
    # To recognize Devanagari script
    pod 'GoogleMLKit/TextRecognitionDevanagari', '3.2.0'
    # To recognize Japanese script
    pod 'GoogleMLKit/TextRecognitionJapanese', '3.2.0'
    # To recognize Korean script
    pod 'GoogleMLKit/TextRecognitionKorean', '3.2.0'
    
  2. אחרי ההתקנה או העדכון של Pods של הפרויקט, פותחים את פרויקט Xcode באמצעות .xcworkspace שלו. קיימת תמיכה ב-ML Kit בגרסה Xcode 12.4 ואילך.

1. יצירת מכונה של TextRecognizer

כדי ליצור מכונה של TextRecognizer, צריך לבצע קריאה ל-+textRecognizer(options:) ולהעביר את האפשרויות שקשורות ל-SDK שהצהרתם עליו כתלויות:

Swift

// When using Latin script recognition SDK
let latinOptions = TextRecognizerOptions()
let latinTextRecognizer = TextRecognizer.textRecognizer(options:options)

// When using Chinese script recognition SDK
let chineseOptions = ChineseTextRecognizerOptions()
let chineseTextRecognizer = TextRecognizer.textRecognizer(options:options)

// When using Devanagari script recognition SDK
let devanagariOptions = DevanagariTextRecognizerOptions()
let devanagariTextRecognizer = TextRecognizer.textRecognizer(options:options)

// When using Japanese script recognition SDK
let japaneseOptions = JapaneseTextRecognizerOptions()
let japaneseTextRecognizer = TextRecognizer.textRecognizer(options:options)

// When using Korean script recognition SDK
let koreanOptions = KoreanTextRecognizerOptions()
let koreanTextRecognizer = TextRecognizer.textRecognizer(options:options)

Objective-C

// When using Latin script recognition SDK
MLKTextRecognizerOptions *latinOptions = [[MLKTextRecognizerOptions alloc] init];
MLKTextRecognizer *latinTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

// When using Chinese script recognition SDK
MLKChineseTextRecognizerOptions *chineseOptions = [[MLKChineseTextRecognizerOptions alloc] init];
MLKTextRecognizer *chineseTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

// When using Devanagari script recognition SDK
MLKDevanagariTextRecognizerOptions *devanagariOptions = [[MLKDevanagariTextRecognizerOptions alloc] init];
MLKTextRecognizer *devanagariTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

// When using Japanese script recognition SDK
MLKJapaneseTextRecognizerOptions *japaneseOptions = [[MLKJapaneseTextRecognizerOptions alloc] init];
MLKTextRecognizer *japaneseTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

// When using Korean script recognition SDK
MLKKoreanTextRecognizerOptions *koreanOptions = [[MLKKoreanTextRecognizerOptions alloc] init];
MLKTextRecognizer *koreanTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

2. מכינים את תמונת הקלט

צריך להעביר את התמונה כ-UIImage או CMSampleBufferRef אל שיטת process(_:completion:) של TextRecognizer:

יצירת אובייקט 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. עיבוד התמונה

לאחר מכן, צריך להעביר את התמונה למתודה process(_:completion:):

Swift

textRecognizer.process(visionImage) { result, error in
  guard error == nil, let result = result else {
    // Error handling
    return
  }
  // Recognized text
}

Objective-C

[textRecognizer processImage:image
                  completion:^(MLKText *_Nullable result,
                               NSError *_Nullable error) {
  if (error != nil || result == nil) {
    // Error handling
    return;
  }
  // Recognized text
}];

4. חילוץ טקסט מבלוקים של טקסט מזוהה

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

כל TextBlock מייצג בלוק מלבני של טקסט, שמכיל אפס או יותר אובייקטים של TextLine. כל אובייקט TextLine מכיל אפס או יותר אובייקטים מסוג TextElement, שמייצגים מילים וישויות דמויי מילים, כמו תאריכים ומספרים.

בכל אובייקט TextBlock, TextLine ואובייקט TextElement, אפשר לקבל את הטקסט המזוהה באזור ואת הקואורדינטות התוחמות של האזור.

למשל:

Swift

let resultText = result.text
for block in result.blocks {
    let blockText = block.text
    let blockLanguages = block.recognizedLanguages
    let blockCornerPoints = block.cornerPoints
    let blockFrame = block.frame
    for line in block.lines {
        let lineText = line.text
        let lineLanguages = line.recognizedLanguages
        let lineCornerPoints = line.cornerPoints
        let lineFrame = line.frame
        for element in line.elements {
            let elementText = element.text
            let elementCornerPoints = element.cornerPoints
            let elementFrame = element.frame
        }
    }
}

Objective-C

NSString *resultText = result.text;
for (MLKTextBlock *block in result.blocks) {
  NSString *blockText = block.text;
  NSArray<MLKTextRecognizedLanguage *> *blockLanguages = block.recognizedLanguages;
  NSArray<NSValue *> *blockCornerPoints = block.cornerPoints;
  CGRect blockFrame = block.frame;
  for (MLKTextLine *line in block.lines) {
    NSString *lineText = line.text;
    NSArray<MLKTextRecognizedLanguage *> *lineLanguages = line.recognizedLanguages;
    NSArray<NSValue *> *lineCornerPoints = line.cornerPoints;
    CGRect lineFrame = line.frame;
    for (MLKTextElement *element in line.elements) {
      NSString *elementText = element.text;
      NSArray<NSValue *> *elementCornerPoints = element.cornerPoints;
      CGRect elementFrame = element.frame;
    }
  }
}

הנחיות להזנת תמונה

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

    לדוגמה, תמונה בגודל 640x480 עשויה להתאים לסריקת כרטיס ביקור ברוחב מלא של התמונה. כדי לסרוק מסמך מודפס על נייר בגודל אות, ייתכן שיהיה צורך בתמונה בגודל 720x1280 פיקסלים.

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

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

טיפים לשיפור הביצועים

  • לעיבוד מסגרות וידאו, יש להשתמש ב-API הסינכרוני של results(in:) של המזהה. אפשר לקרוא לשיטה הזו על ידי הפונקציה captureOutput(_, didOutput:from:) של AVCaptureVideoDataOutputSampleBufferDelegate כדי לקבל תוצאות סינכרוניות ממסגרת הווידאו הנתונה. צריך להשאיר את האפליקציה alwaysDiscardsLateVideoFrames של AVCaptureVideoDataOutput בתור true כדי לווסת את הקריאות למזהה. אם פריים חדש של סרטון יהיה זמין בזמן שהמזהה פועל, הוא יוסר.
  • אם משתמשים בפלט של המזהה כדי להוסיף גרפיקה כשכבת-על בתמונת הקלט, קודם צריך לקבל את התוצאה מ-ML Kit ואז לעבד את התמונה ואת שכבת-העל בפעולה אחת. לאחר מכן, תוכלו לעבד את שטח התצוגה פעם אחת בלבד בכל מסגרת קלט שעובדה. תוכלו לעיין ב-updatePreviewLayerViewWithLastFrame שמופיע בדוגמה למתחילים של ML Kit.
  • כדאי לצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב גם לזכור את הדרישות לגבי מידות התמונה ב-API הזה.
  • כדי למנוע פגיעה אפשרית בביצועים, אין להריץ כמה מופעים של TextRecognizer בו-זמנית עם אפשרויות שונות של סקריפטים.