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

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

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

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

  • מומלץ להתנסות באפליקציה לדוגמה כדי לראות דוגמה לשימוש ב-API הזה.
  • אתם יכולים לנסות את הקוד בעצמכם באמצעות Codelab.

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

  1. כוללים את רצפי ה-ML הבאים ב-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. אחרי שמתקינים או מעדכנים את קבוצות ה-Pod של הפרויקט, פותחים את פרויקט 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 ל-method 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. עיבוד התמונה

לאחר מכן, מעבירים את התמונה ל-method 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:) של הגלאי. מפעילים את ה-method הזה מהפונקציה captureOutput(_, didOutput:from:) של AVCaptureVideoDataOutputSampleBufferDelegate כדי לקבל באופן סינכרוני תוצאות מהפריים הנתון של הווידאו. צריך להשאיר את alwaysDiscardsLateVideoFrames של AVCaptureVideoDataOutput בתור true כדי לווסת את הקריאות לגלאי. אם פריים חדש של וידאו יהיה זמין בזמן שהגלאי פועל, היא תוסר.
  • אם משתמשים בפלט של הגלאי כדי ליצור שכבת-על של גרפיקה בתמונת הקלט, מקבלים קודם את התוצאה מ-ML Kit ואז מעבדים את התמונה ושכבת-העל בפעולה אחת. כך אפשר לעבד את משטח המסך פעם אחת בלבד לכל מסגרת קלט שעברה עיבוד. כדוגמה, אפשר לעיין ב-updatePreviewOverlayViewWithLastFrame בדוגמה של המדריך למתחילים ל-ML Kit.
  • כדאי לצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב גם לזכור את הדרישות לגבי מידות התמונה של ה-API.
  • כדי למנוע פגיעה פוטנציאלית בביצועים, אין להריץ כמה מכונות TextRecognizer עם אפשרויות שונות של סקריפטים בו-זמנית.