אפשר להשתמש ב-ML Kit כדי לזהות טקסט בתמונות. ML Kit כולל גם API לשימוש כללי שמתאים לזיהוי טקסט בתמונות, כמו טקסט של תמרור, וגם API שעבר אופטימיזציה לזיהוי טקסט במסמכים. ל-API לשימוש כללי יש מודלים במכשיר ומודלים מבוססי-ענן. זיהוי טקסט במסמך זמין רק כמודל מבוסס-ענן. בסקירה הכללית מופיעה השוואה בין המודלים בענן ובמכשיר.
לפני שמתחילים
- אם עדיין לא הוספתם את Firebase לאפליקציה, אתם צריכים לעשות זאת לפי השלבים במדריך לתחילת העבודה.
- מוסיפים את ספריות ML Kit ל-Podfile:
אחרי שמתקינים או מעדכנים את ה-Pods של הפרויקט, חשוב לפתוח את פרויקט Xcode באמצעותpod 'Firebase/MLVision', '6.25.0' # If using an on-device API: pod 'Firebase/MLVisionTextModel', '6.25.0'
.xcworkspace
. - באפליקציה, מייבאים את Firebase:
Swift
import Firebase
Objective-C
@import Firebase;
-
אם אתם רוצים להשתמש במודל מבוסס-Cloud, ולא הפעלתם עדיין את ממשקי ה-API מבוססי-Cloud בפרויקט שלכם, עכשיו זה הזמן:
- פותחים את הדף ML Kit APIs במסוף Firebase.
-
אם עדיין לא שדרגתם את הפרויקט לתוכנית התמחור Blaze, לוחצים על שדרוג כדי לעשות זאת. (ההודעה על הצורך בשדרוג תוצג רק אם הפרויקט לא מוגדר בתוכנית Blaze).
רק בפרויקטים ברמת Blaze אפשר להשתמש בממשקי API מבוססי-Cloud.
- אם ממשקי API מבוססי-ענן לא מופעלים כבר, לוחצים על Enable Cloud-based APIs.
אם רוצים להשתמש רק במודל במכשיר, אפשר לדלג על השלב הזה.
עכשיו אפשר להתחיל לזהות טקסט בתמונות.
הנחיות לגבי תמונות קלט
-
כדי ש-ML Kit יזהה טקסט בצורה מדויקת, התמונות שמוזנות לו צריכות להכיל טקסט שמיוצג על ידי נתוני פיקסלים מספיקים. באופן אידיאלי, עבור טקסט לטיני, כל תו צריך להיות בגודל 16x16 פיקסלים לפחות. בטקסט בסינית, ביפנית ובקוריאנית (שנתמך רק על ידי ממשקי ה-API מבוססי-הענן), כל תו צריך להיות בגודל 24x24 פיקסלים. בכל השפות, בדרך כלל אין יתרון בדיוק אם התווים גדולים מ-24x24 פיקסלים.
לדוגמה, תמונה בגודל 640x480 יכולה להתאים לסריקה של כרטיס ביקור שממלא את כל הרוחב של התמונה. כדי לסרוק מסמך שמודפס על נייר בגודל Letter, יכול להיות שתידרש תמונה בגודל 720x1280 פיקסלים.
-
פוקוס לא טוב של התמונה עלול לפגוע בדיוק של זיהוי הטקסט. אם התוצאות לא מספיק טובות, אפשר לבקש מהמשתמש לצלם מחדש את התמונה.
-
אם אתם מזהים טקסט באפליקציה בזמן אמת, כדאי גם לקחת בחשבון את הממדים הכוללים של תמונות הקלט. אפשר לעבד תמונות קטנות יותר מהר יותר, ולכן כדי לצמצם את זמן האחזור, כדאי לצלם תמונות ברזולוציות נמוכות יותר (תוך הקפדה על דרישות הדיוק שצוינו למעלה) ולוודא שהטקסט תופס כמה שיותר מהתמונה. כדאי לעיין גם בטיפים לשיפור הביצועים בזמן אמת.
זיהוי טקסט בתמונות
כדי לזהות טקסט בתמונה באמצעות מודל מבוסס-ענן או מודל שפועל במכשיר, מפעילים את הכלי לזיהוי טקסט כמו שמתואר בהמשך.
1. הפעלת הכלי לזיהוי טקסט
מעבירים את התמונה כ-`UIImage` או כ-`CMSampleBufferRef` לשיטה `process(_:completion:)` של `VisionTextRecognizer`:- כדי לקבל מופע של
VisionTextRecognizer
, קוראים לאחת מהפונקציות הבאות:onDeviceTextRecognizer
אוcloudTextRecognizer
:Swift
כדי להשתמש במודל במכשיר:
let vision = Vision.vision() let textRecognizer = vision.onDeviceTextRecognizer()
כדי להשתמש במודל הענן:
let vision = Vision.vision() let textRecognizer = vision.cloudTextRecognizer() // Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages let options = VisionCloudTextRecognizerOptions() options.languageHints = ["en", "hi"] let textRecognizer = vision.cloudTextRecognizer(options: options)
Objective-C
כדי להשתמש במודל במכשיר:
FIRVision *vision = [FIRVision vision]; FIRVisionTextRecognizer *textRecognizer = [vision onDeviceTextRecognizer];
כדי להשתמש במודל הענן:
FIRVision *vision = [FIRVision vision]; FIRVisionTextRecognizer *textRecognizer = [vision cloudTextRecognizer]; // Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages FIRVisionCloudTextRecognizerOptions *options = [[FIRVisionCloudTextRecognizerOptions alloc] init]; options.languageHints = @[@"en", @"hi"]; FIRVisionTextRecognizer *textRecognizer = [vision cloudTextRecognizerWithOptions:options];
-
יוצרים אובייקט
VisionImage
באמצעותUIImage
אוCMSampleBufferRef
.כדי להשתמש ב-
UIImage
:- אם צריך, מסובבים את התמונה כך שהמאפיין
imageOrientation
שלה יהיה.up
. - יוצרים אובייקט
VisionImage
באמצעותUIImage
מסובב בצורה נכונה. לא מציינים מטא-נתונים של סיבוב – צריך להשתמש בערך ברירת המחדל,.topLeft
.Swift
let image = VisionImage(image: uiImage)
Objective-C
FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];
כדי להשתמש ב-
CMSampleBufferRef
:-
יוצרים אובייקט
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];
- יוצרים אובייקט
VisionImage
באמצעות האובייקטCMSampleBufferRef
ומטא-נתוני הסיבוב:Swift
let image = VisionImage(buffer: sampleBuffer) image.metadata = metadata
Objective-C
FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer]; image.metadata = metadata;
- אם צריך, מסובבים את התמונה כך שהמאפיין
-
לאחר מכן, מעבירים את התמונה לשיטה
process(_:completion:)
:Swift
textRecognizer.process(visionImage) { result, error in guard error == nil, let result = result else { // ... return } // Recognized text }
Objective-C
[textRecognizer processImage:image completion:^(FIRVisionText *_Nullable result, NSError *_Nullable error) { if (error != nil || result == nil) { // ... return; } // Recognized text }];
2. חילוץ טקסט מבלוקים של טקסט מזוהה
אם פעולת זיהוי הטקסט תצליח, יוחזר אובייקט [<code>VisionText</code>][VisionText]. אובייקט `VisionText` מכיל את הטקסט המלא שזוהה בתמונה, ואפס או יותר אובייקטים מסוג [`VisionTextBlock`][VisionTextBlock]. כל `VisionTextBlock` מייצג בלוק טקסט מלבני, שמכיל אפס או יותר אובייקטים מסוג [`VisionTextLine`][VisionTextLine]. כל אובייקט `VisionTextLine` מכיל אפס או יותר אובייקטים מסוג [`VisionTextElement`][VisionTextElement], שמייצגים מילים וישויות דמויות מילים (תאריכים, מספרים וכו'). לכל אובייקט `VisionTextBlock`, `VisionTextLine` ו-`VisionTextElement`, אפשר לקבל את הטקסט שזוהה באזור ואת קואורדינטות התיחום של האזור. לדוגמה:Swift
let resultText = result.text for block in result.blocks { let blockText = block.text let blockConfidence = block.confidence let blockLanguages = block.recognizedLanguages let blockCornerPoints = block.cornerPoints let blockFrame = block.frame for line in block.lines { let lineText = line.text let lineConfidence = line.confidence let lineLanguages = line.recognizedLanguages let lineCornerPoints = line.cornerPoints let lineFrame = line.frame for element in line.elements { let elementText = element.text let elementConfidence = element.confidence let elementLanguages = element.recognizedLanguages let elementCornerPoints = element.cornerPoints let elementFrame = element.frame } } }
Objective-C
NSString *resultText = result.text; for (FIRVisionTextBlock *block in result.blocks) { NSString *blockText = block.text; NSNumber *blockConfidence = block.confidence; NSArray<FIRVisionTextRecognizedLanguage *> *blockLanguages = block.recognizedLanguages; NSArray<NSValue *> *blockCornerPoints = block.cornerPoints; CGRect blockFrame = block.frame; for (FIRVisionTextLine *line in block.lines) { NSString *lineText = line.text; NSNumber *lineConfidence = line.confidence; NSArray<FIRVisionTextRecognizedLanguage *> *lineLanguages = line.recognizedLanguages; NSArray<NSValue *> *lineCornerPoints = line.cornerPoints; CGRect lineFrame = line.frame; for (FIRVisionTextElement *element in line.elements) { NSString *elementText = element.text; NSNumber *elementConfidence = element.confidence; NSArray<FIRVisionTextRecognizedLanguage *> *elementLanguages = element.recognizedLanguages; NSArray<NSValue *> *elementCornerPoints = element.cornerPoints; CGRect elementFrame = element.frame; } } }
טיפים לשיפור הביצועים בזמן אמת
אם רוצים להשתמש במודל במכשיר כדי לזהות טקסט באפליקציה בזמן אמת, כדאי לפעול לפי ההנחיות האלה כדי להשיג את קצב הפריימים הטוב ביותר:
- הגבלת מספר השיחות לזיהוי הטקסט. אם פריים חדש של סרטון הופך לזמין בזמן שרכיב זיהוי הטקסט פועל, צריך להשליך את הפריים.
- אם אתם משתמשים בפלט של הכלי לזיהוי טקסט כדי להוסיף גרפיקה לתמונת הקלט, קודם צריך לקבל את התוצאה מ-ML Kit, ואז לעבד את התמונה ולהוסיף את הגרפיקה בשלב אחד. כך, הרינדור מתבצע רק פעם אחת לכל מסגרת קלט. דוגמה אפשר לראות במחלקות previewOverlayView ו-FIRDetectionOverlayView באפליקציית הדוגמה showcase.
- כדאי לצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב לזכור גם את הדרישות לגבי מידות התמונה ב-API הזה.
השלבים הבאים
- לפני שפורסים לייצור אפליקציה שמשתמשת ב-Cloud API, כדאי לבצע כמה שלבים נוספים כדי למנוע גישה לא מורשית ל-API ולצמצם את ההשפעה שלה.
זיהוי טקסט בתמונות של מסמכים
כדי לזהות את הטקסט במסמך, צריך להגדיר ולהפעיל את הכלי לזיהוי טקסט במסמכים שמבוסס על ענן, כמו שמתואר בהמשך.
ממשק ה-API לזיהוי טקסט במסמכים, שמתואר בהמשך, נועד להקל על העבודה עם תמונות של מסמכים. אבל אם אתם מעדיפים את הממשק שמספק ה-API של טקסט דליל, אתם יכולים להשתמש בו במקום זאת כדי לסרוק מסמכים. לשם כך, צריך להגדיר את הכלי לזיהוי טקסט בענן לשימוש במודל של טקסט צפוף.
כדי להשתמש ב-API לזיהוי טקסט במסמך:
1. הפעלת הכלי לזיהוי טקסט
מעבירים את התמונה כ-UIImage
או כ-CMSampleBufferRef
לשיטה process(_:completion:)
של VisionDocumentTextRecognizer
:
- מקבלים מופע של
VisionDocumentTextRecognizer
על ידי קריאה ל-cloudDocumentTextRecognizer
:Swift
let vision = Vision.vision() let textRecognizer = vision.cloudDocumentTextRecognizer() // Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages let options = VisionCloudDocumentTextRecognizerOptions() options.languageHints = ["en", "hi"] let textRecognizer = vision.cloudDocumentTextRecognizer(options: options)
Objective-C
FIRVision *vision = [FIRVision vision]; FIRVisionDocumentTextRecognizer *textRecognizer = [vision cloudDocumentTextRecognizer]; // Or, to provide language hints to assist with language detection: // See https://cloud.google.com/vision/docs/languages for supported languages FIRVisionCloudDocumentTextRecognizerOptions *options = [[FIRVisionCloudDocumentTextRecognizerOptions alloc] init]; options.languageHints = @[@"en", @"hi"]; FIRVisionDocumentTextRecognizer *textRecognizer = [vision cloudDocumentTextRecognizerWithOptions:options];
-
יוצרים אובייקט
VisionImage
באמצעותUIImage
אוCMSampleBufferRef
.כדי להשתמש ב-
UIImage
:- אם צריך, מסובבים את התמונה כך שהמאפיין
imageOrientation
שלה יהיה.up
. - יוצרים אובייקט
VisionImage
באמצעותUIImage
מסובב בצורה נכונה. לא מציינים מטא-נתונים של סיבוב – צריך להשתמש בערך ברירת המחדל,.topLeft
.Swift
let image = VisionImage(image: uiImage)
Objective-C
FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];
כדי להשתמש ב-
CMSampleBufferRef
:-
יוצרים אובייקט
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];
- יוצרים אובייקט
VisionImage
באמצעות האובייקטCMSampleBufferRef
ומטא-נתוני הסיבוב:Swift
let image = VisionImage(buffer: sampleBuffer) image.metadata = metadata
Objective-C
FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer]; image.metadata = metadata;
- אם צריך, מסובבים את התמונה כך שהמאפיין
-
לאחר מכן, מעבירים את התמונה לשיטה
process(_:completion:)
:Swift
textRecognizer.process(visionImage) { result, error in guard error == nil, let result = result else { // ... return } // Recognized text }
Objective-C
[textRecognizer processImage:image completion:^(FIRVisionDocumentText *_Nullable result, NSError *_Nullable error) { if (error != nil || result == nil) { // ... return; } // Recognized text }];
2. חילוץ טקסט מבלוקים של טקסט מזוהה
אם פעולת זיהוי הטקסט תצליח, יוחזר אובייקטVisionDocumentText
. אובייקט VisionDocumentText
מכיל את הטקסט המלא שזוהה בתמונה ואת ההיררכיה של האובייקטים שמשקפים את המבנה של המסמך שזוהה:
לכל אובייקט VisionDocumentTextBlock
, VisionDocumentTextParagraph
, VisionDocumentTextWord
ו-VisionDocumentTextSymbol
, אפשר לקבל את הטקסט שזוהה באזור ואת קואורדינטות התיחום של האזור.
לדוגמה:
Swift
let resultText = result.text for block in result.blocks { let blockText = block.text let blockConfidence = block.confidence let blockRecognizedLanguages = block.recognizedLanguages let blockBreak = block.recognizedBreak let blockCornerPoints = block.cornerPoints let blockFrame = block.frame for paragraph in block.paragraphs { let paragraphText = paragraph.text let paragraphConfidence = paragraph.confidence let paragraphRecognizedLanguages = paragraph.recognizedLanguages let paragraphBreak = paragraph.recognizedBreak let paragraphCornerPoints = paragraph.cornerPoints let paragraphFrame = paragraph.frame for word in paragraph.words { let wordText = word.text let wordConfidence = word.confidence let wordRecognizedLanguages = word.recognizedLanguages let wordBreak = word.recognizedBreak let wordCornerPoints = word.cornerPoints let wordFrame = word.frame for symbol in word.symbols { let symbolText = symbol.text let symbolConfidence = symbol.confidence let symbolRecognizedLanguages = symbol.recognizedLanguages let symbolBreak = symbol.recognizedBreak let symbolCornerPoints = symbol.cornerPoints let symbolFrame = symbol.frame } } } }
Objective-C
NSString *resultText = result.text; for (FIRVisionDocumentTextBlock *block in result.blocks) { NSString *blockText = block.text; NSNumber *blockConfidence = block.confidence; NSArray<FIRVisionTextRecognizedLanguage *> *blockRecognizedLanguages = block.recognizedLanguages; FIRVisionTextRecognizedBreak *blockBreak = block.recognizedBreak; CGRect blockFrame = block.frame; for (FIRVisionDocumentTextParagraph *paragraph in block.paragraphs) { NSString *paragraphText = paragraph.text; NSNumber *paragraphConfidence = paragraph.confidence; NSArray<FIRVisionTextRecognizedLanguage *> *paragraphRecognizedLanguages = paragraph.recognizedLanguages; FIRVisionTextRecognizedBreak *paragraphBreak = paragraph.recognizedBreak; CGRect paragraphFrame = paragraph.frame; for (FIRVisionDocumentTextWord *word in paragraph.words) { NSString *wordText = word.text; NSNumber *wordConfidence = word.confidence; NSArray<FIRVisionTextRecognizedLanguage *> *wordRecognizedLanguages = word.recognizedLanguages; FIRVisionTextRecognizedBreak *wordBreak = word.recognizedBreak; CGRect wordFrame = word.frame; for (FIRVisionDocumentTextSymbol *symbol in word.symbols) { NSString *symbolText = symbol.text; NSNumber *symbolConfidence = symbol.confidence; NSArray<FIRVisionTextRecognizedLanguage *> *symbolRecognizedLanguages = symbol.recognizedLanguages; FIRVisionTextRecognizedBreak *symbolBreak = symbol.recognizedBreak; CGRect symbolFrame = symbol.frame; } } } }
השלבים הבאים
- לפני שפורסים לייצור אפליקציה שמשתמשת ב-Cloud API, כדאי לבצע כמה שלבים נוספים כדי למנוע גישה לא מורשית ל-API ולצמצם את ההשפעה שלה.