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

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

ממשק API לזיהוי טקסט
תיאורזיהוי טקסט לטיני בתמונות או בסרטונים.
שם ה-SDKGoogleMLKit/TextRecognition (version 2.2.0)
יישוםהנכסים מקושרים באופן סטטי לאפליקציה שלך בשלב הבנייה.
ההשפעה על גודל האפליקציהכ-20MB
ביצועיםזמן אמת ברוב המכשירים.

רוצה לנסות?

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

  1. יש לכלול את ה-pods הבאים ב-ML Kit ב-Podfile:
    pod 'GoogleMLKit/TextRecognition','2.2.0'
    
  2. אחרי ההתקנה או העדכון של ה-Pods של הפרויקט, פותחים את פרויקט Xcode באמצעות .xcworkspace שלו. ערכת ה-ML נתמכת ב-Xcode מגרסה 12.4 ואילך.

1. יצירת מופע של TextRecognizer

יצירת מכונה של TextRecognizer על ידי התקשרות ל-+textRecognizer:

Swift

let textRecognizer = TextRecognizer.textRecognizer()
      

Objective-C

MLKTextRecognizer *textRecognizer = [MLKTextRecognizer textRecognizer];
      

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 פיקסלים.

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

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

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

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