Mit der digitalen Tintenerkennung von ML Kit können Sie handschriftlichen Text auf digitalen Oberflächen in Hunderten von Sprachen verfügbar machen und Skizzen klassifizieren.
Jetzt ausprobieren
- Probieren Sie die Beispiel-App aus, um sehen Sie sich ein Anwendungsbeispiel für diese API an.
Hinweis
Fügen Sie die folgenden ML Kit-Bibliotheken in Ihre Podfile-Datei ein:
pod 'GoogleMLKit/DigitalInkRecognition', '3.2.0'
Öffnen Sie nach der Installation oder Aktualisierung der Pods Ihres Projekts das Xcode-Projekt. mit
.xcworkspace
. ML Kit wird in der Xcode-Version unterstützt 13.2.1 oder höher.
Sie können jetzt mit der Texterkennung in Ink
-Objekten beginnen.
Ink
-Objekt erstellen
Die Hauptmethode zum Erstellen eines Ink
-Objekts besteht darin, es auf einen Touchscreen zu zeichnen. Auf iOS-Geräten
können Sie UIImageView zusammen mit
Touch-Event
Handler
die die Striche auf den Bildschirm zeichnen und Punkte zum Erstellen
Ink
. Dieses allgemeine Muster wird im folgenden Code veranschaulicht:
Snippet. Kurzanleitung ansehen
App für eine
in dem die Handhabung von Touch-Ereignissen, Bildschirmzeichnungen
und Schlaganfalldatenmanagement.
Swift
@IBOutlet weak var mainImageView: UIImageView! var kMillisecondsPerTimeInterval = 1000.0 var lastPoint = CGPoint.zero private var strokes: [Stroke] = [] private var points: [StrokePoint] = [] func drawLine(from fromPoint: CGPoint, to toPoint: CGPoint) { UIGraphicsBeginImageContext(view.frame.size) guard let context = UIGraphicsGetCurrentContext() else { return } mainImageView.image?.draw(in: view.bounds) context.move(to: fromPoint) context.addLine(to: toPoint) context.setLineCap(.round) context.setBlendMode(.normal) context.setLineWidth(10.0) context.setStrokeColor(UIColor.white.cgColor) context.strokePath() mainImageView.image = UIGraphicsGetImageFromCurrentImageContext() mainImageView.alpha = 1.0 UIGraphicsEndImageContext() } override func touchesBegan(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first else { return } lastPoint = touch.location(in: mainImageView) let t = touch.timestamp points = [StrokePoint.init(x: Float(lastPoint.x), y: Float(lastPoint.y), t: Int(t * kMillisecondsPerTimeInterval))] drawLine(from:lastPoint, to:lastPoint) } override func touchesMoved(_ touches: Set , with event: UIEvent?) { guard let touch = touches.first else { return } let currentPoint = touch.location(in: mainImageView) let t = touch.timestamp points.append(StrokePoint.init(x: Float(currentPoint.x), y: Float(currentPoint.y), t: Int(t * kMillisecondsPerTimeInterval))) drawLine(from: lastPoint, to: currentPoint) lastPoint = currentPoint } override func touchesEnded(_ touches: Set , with event: UIEvent?) { guard let touch = touches.first else { return } let currentPoint = touch.location(in: mainImageView) let t = touch.timestamp points.append(StrokePoint.init(x: Float(currentPoint.x), y: Float(currentPoint.y), t: Int(t * kMillisecondsPerTimeInterval))) drawLine(from: lastPoint, to: currentPoint) lastPoint = currentPoint strokes.append(Stroke.init(points: points)) self.points = [] doRecognition() }
Objective-C
// Interface @property (weak, nonatomic) IBOutlet UIImageView *mainImageView; @property(nonatomic) CGPoint lastPoint; @property(nonatomic) NSMutableArray*strokes; @property(nonatomic) NSMutableArray *points; // Implementations static const double kMillisecondsPerTimeInterval = 1000.0; - (void)drawLineFrom:(CGPoint)fromPoint to:(CGPoint)toPoint { UIGraphicsBeginImageContext(self.mainImageView.frame.size); [self.mainImageView.image drawInRect:CGRectMake(0, 0, self.mainImageView.frame.size.width, self.mainImageView.frame.size.height)]; CGContextMoveToPoint(UIGraphicsGetCurrentContext(), fromPoint.x, fromPoint.y); CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), toPoint.x, toPoint.y); CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound); CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 10.0); CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1, 1, 1, 1); CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal); CGContextStrokePath(UIGraphicsGetCurrentContext()); CGContextFlush(UIGraphicsGetCurrentContext()); self.mainImageView.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } - (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event { UITouch *touch = [touches anyObject]; self.lastPoint = [touch locationInView:self.mainImageView]; NSTimeInterval time = [touch timestamp]; self.points = [NSMutableArray array]; [self.points addObject:[[MLKStrokePoint alloc] initWithX:self.lastPoint.x y:self.lastPoint.y t:time * kMillisecondsPerTimeInterval]]; [self drawLineFrom:self.lastPoint to:self.lastPoint]; } - (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint currentPoint = [touch locationInView:self.mainImageView]; NSTimeInterval time = [touch timestamp]; [self.points addObject:[[MLKStrokePoint alloc] initWithX:currentPoint.x y:currentPoint.y t:time * kMillisecondsPerTimeInterval]]; [self drawLineFrom:self.lastPoint to:currentPoint]; self.lastPoint = currentPoint; } - (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint currentPoint = [touch locationInView:self.mainImageView]; NSTimeInterval time = [touch timestamp]; [self.points addObject:[[MLKStrokePoint alloc] initWithX:currentPoint.x y:currentPoint.y t:time * kMillisecondsPerTimeInterval]]; [self drawLineFrom:self.lastPoint to:currentPoint]; self.lastPoint = currentPoint; if (self.strokes == nil) { self.strokes = [NSMutableArray array]; } [self.strokes addObject:[[MLKStroke alloc] initWithPoints:self.points]]; self.points = nil; [self doRecognition]; }
Beachten Sie, dass das Code-Snippet eine Beispielfunktion enthält, um den Strich in
UIImageView
die nach Bedarf an Ihre Anwendung angepasst werden sollten. Wir empfehlen die Verwendung von
beim Zeichnen der Liniensegmente, sodass Segmente der Länge Null
als Punkt gezeichnet (z. B. den Punkt auf einem Kleinbuchstaben i). Das doRecognition()
-Funktion wird aufgerufen, nachdem jeder Strich geschrieben wurde, und wird unten definiert.
Instanz von DigitalInkRecognizer
abrufen
Für eine Erkennung muss das Ink
-Objekt an ein
DigitalInkRecognizer
-Instanz. So rufen Sie die Instanz DigitalInkRecognizer
ab:
müssen wir zuerst das Erkennungsmodell für die gewünschte Sprache herunterladen.
das Modell in den RAM zu laden. Dies kann mithilfe des folgenden Codes erreicht werden:
Snippet, das der Einfachheit halber in die viewDidLoad()
-Methode eingefügt wird und ein
hartcodierter Sprachname. Kurzanleitung ansehen
App für eine
Beispiel für die Anzeige der Liste verfügbarer Sprachen für den Nutzer und den Download
in der ausgewählten Sprache.
Swift
override func viewDidLoad() { super.viewDidLoad() let languageTag = "en-US" let identifier = DigitalInkRecognitionModelIdentifier(forLanguageTag: languageTag) if identifier == nil { // no model was found or the language tag couldn't be parsed, handle error. } let model = DigitalInkRecognitionModel.init(modelIdentifier: identifier!) let modelManager = ModelManager.modelManager() let conditions = ModelDownloadConditions.init(allowsCellularAccess: true, allowsBackgroundDownloading: true) modelManager.download(model, conditions: conditions) // Get a recognizer for the language let options: DigitalInkRecognizerOptions = DigitalInkRecognizerOptions.init(model: model) recognizer = DigitalInkRecognizer.digitalInkRecognizer(options: options) }
Objective-C
- (void)viewDidLoad { [super viewDidLoad]; NSString *languagetag = @"en-US"; MLKDigitalInkRecognitionModelIdentifier *identifier = [MLKDigitalInkRecognitionModelIdentifier modelIdentifierForLanguageTag:languagetag]; if (identifier == nil) { // no model was found or the language tag couldn't be parsed, handle error. } MLKDigitalInkRecognitionModel *model = [[MLKDigitalInkRecognitionModel alloc] initWithModelIdentifier:identifier]; MLKModelManager *modelManager = [MLKModelManager modelManager]; [modelManager downloadModel:model conditions:[[MLKModelDownloadConditions alloc] initWithAllowsCellularAccess:YES allowsBackgroundDownloading:YES]]; MLKDigitalInkRecognizerOptions *options = [[MLKDigitalInkRecognizerOptions alloc] initWithModel:model]; self.recognizer = [MLKDigitalInkRecognizer digitalInkRecognizerWithOptions:options]; }
Die Schnellstart-Anwendungen enthalten zusätzlichen Code, der zeigt, wie mehrere Downloads gleichzeitig heruntergeladen werden können und wie Sie feststellen, welcher Download erfolgreich war, Fertigstellungsbenachrichtigungen.
Ink
-Objekt erkennen
Als Nächstes kommt die Funktion doRecognition()
, die der Einfachheit halber
von touchesEnded()
. In anderen Anwendungen empfiehlt es sich,
Erkennung erst nach Ablauf einer Zeitüberschreitung oder wenn der Nutzer eine Schaltfläche zum Auslösen einer
Anerkennung.
Swift
func doRecognition() { let ink = Ink.init(strokes: strokes) recognizer.recognize( ink: ink, completion: { [unowned self] (result: DigitalInkRecognitionResult?, error: Error?) in var alertTitle = "" var alertText = "" if let result = result, let candidate = result.candidates.first { alertTitle = "I recognized this:" alertText = candidate.text } else { alertTitle = "I hit an error:" alertText = error!.localizedDescription } let alert = UIAlertController(title: alertTitle, message: alertText, preferredStyle: UIAlertController.Style.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) self.present(alert, animated: true, completion: nil) } ) }
Objective-C
- (void)doRecognition { MLKInk *ink = [[MLKInk alloc] initWithStrokes:self.strokes]; __weak typeof(self) weakSelf = self; [self.recognizer recognizeInk:ink completion:^(MLKDigitalInkRecognitionResult *_Nullable result, NSError *_Nullable error) { typeof(weakSelf) strongSelf = weakSelf; if (strongSelf == nil) { return; } NSString *alertTitle = nil; NSString *alertText = nil; if (result.candidates.count > 0) { alertTitle = @"I recognized this:"; alertText = result.candidates[0].text; } else { alertTitle = @"I hit an error:"; alertText = [error localizedDescription]; } UIAlertController *alert = [UIAlertController alertControllerWithTitle:alertTitle message:alertText preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; [strongSelf presentViewController:alert animated:YES completion:nil]; }]; }
Modelldownloads verwalten
Wir haben bereits gesehen, wie man ein Erkennungsmodell herunterlädt. Der folgende Code Snippets veranschaulichen, wie geprüft wird, ob ein Modell bereits heruntergeladen wurde, oder um ein Modell zu löschen, wenn es zum Freigeben von Speicherplatz nicht mehr benötigt wird.
Prüfen, ob ein Modell bereits heruntergeladen wurde
Swift
let model : DigitalInkRecognitionModel = ... let modelManager = ModelManager.modelManager() modelManager.isModelDownloaded(model)
Objective-C
MLKDigitalInkRecognitionModel *model = ...; MLKModelManager *modelManager = [MLKModelManager modelManager]; [modelManager isModelDownloaded:model];
Heruntergeladenes Modell löschen
Swift
let model : DigitalInkRecognitionModel = ... let modelManager = ModelManager.modelManager() if modelManager.isModelDownloaded(model) { modelManager.deleteDownloadedModel( model!, completion: { error in if error != nil { // Handle error return } NSLog(@"Model deleted."); }) }
Objective-C
MLKDigitalInkRecognitionModel *model = ...; MLKModelManager *modelManager = [MLKModelManager modelManager]; if ([self.modelManager isModelDownloaded:model]) { [self.modelManager deleteDownloadedModel:model completion:^(NSError *_Nullable error) { if (error) { // Handle error. return; } NSLog(@"Model deleted."); }]; }
Tipps zur Verbesserung der Genauigkeit der Texterkennung
Die Genauigkeit der Texterkennung kann je nach Sprache variieren. Die Genauigkeit hängt auch von zum Schreibstil. Die digitale Tintenerkennung wurde für viele verschiedene Schreibstile trainiert, können die Ergebnisse von Nutzer zu Nutzer variieren.
Hier finden Sie einige Möglichkeiten, die Genauigkeit einer Texterkennung zu verbessern. Beachten Sie, dass diese Methoden gelten nicht für die Zeichenklassifikatoren für Emojis, AutoDraw und Formen.
Schreibbereich
Viele Anwendungen haben einen klar definierten Schreibbereich für Nutzereingaben. Die Bedeutung eines Symbols teilweise durch seine Größe im Verhältnis zur Größe des Schreibbereichs bestimmt, in dem sie enthalten ist. Zum Beispiel der Unterschied zwischen einem Groß- und Kleinschreibung („O“) oder "c" und ein Komma im Vergleich zu Schrägstrich.
Wenn Sie der Erkennung die Breite und Höhe des Schreibbereichs mitteilen, kann dies die Genauigkeit verbessern. Sie können jedoch wird angenommen, dass der Schreibbereich nur eine einzige Textzeile enthält. Wenn die physische der Schreibbereich groß genug ist, damit Nutzende zwei oder mehr Zeilen schreiben können, Ergebnisse, indem Sie ein WritingArea mit einer Höhe übergeben, die Ihrer besten Schätzung der Höhe eines eine Textzeile schreiben. Das WritingArea-Objekt, das Sie an die Erkennung übergeben, muss nicht mit dem Schreibbereich auf dem Bildschirm. Die Schreibbereichshöhe auf diese Weise ändern funktioniert in manchen Sprachen besser als in anderen.
Wenn Sie den Schreibbereich angeben, geben Sie dessen Breite und Höhe in denselben Einheiten wie der Strich an. Koordinaten. Für die x- und y-Koordinatenargumente gibt es keine Einheitenanforderungen. Die API normalisiert alle -Einheiten, daher ist nur die relative Größe und Position der Striche wichtig. Sie können Folgendes tun: in jedem für Ihr System sinnvollen Maßstab übergeben.
Vor dem Kontext
Der Kontext vor dem Kontext ist der Text, der den Strichen im Ink
, den Sie
zu erkennen. Sie können dem Erkennungsmodul helfen, indem Sie es über den Vorkontext informieren.
Zum Beispiel werden die Schreibschriften „n“ und „u“ werden oft verwechselt. Wenn der Nutzer bereits das Teilwort „arg“ eingegeben hat, werden sie möglicherweise mit Strichen fortgesetzt, die als „Ument“ oder „Nment“. „arg“ im Vorkontext angeben die Ambiguität gelöst, da das Wort „Argument“ wahrscheinlicher als „argnment“.
Vor dem Kontext kann die Erkennung auch dabei helfen, Wortumbrüche, also die Leerzeichen zwischen Wörtern, zu erkennen. Sie können Leerzeichen eingeben, aber nicht zeichnen. Wie kann eine Erkennung feststellen, wann ein Wort endet? und die nächste beginnt? Wenn der Nutzer bereits „hello“ geschrieben hat und fährt mit dem Wort „world“. Ohne Vorkontext gibt die Erkennung den String „world“ zurück. Wenn Sie jedoch die pre-context "hello" enthält, gibt das Modell den String " Welt“ mit einem vorangestellten Leerzeichen, da „Hallo“ Welt“ sinnvoller ist als "Hallowort".
Geben Sie den längstmöglichen Pre-Kontext-String an, der bis zu 20 Zeichen umfasst, einschließlich Leerzeichen. Wenn der String länger ist, verwendet die Erkennung nur die letzten 20 Zeichen.
Das folgende Codebeispiel zeigt, wie Sie einen Schreibbereich definieren und einen
RecognitionContext
-Objekt, um einen Vorkontext anzugeben.
Swift
let ink: Ink = ...; let recognizer: DigitalInkRecognizer = ...; let preContext: String = ...; let writingArea = WritingArea.init(width: ..., height: ...); let context: DigitalInkRecognitionContext.init( preContext: preContext, writingArea: writingArea); recognizer.recognizeHandwriting( from: ink, context: context, completion: { (result: DigitalInkRecognitionResult?, error: Error?) in if let result = result, let candidate = result.candidates.first { NSLog("Recognized \(candidate.text)") } else { NSLog("Recognition error \(error)") } })
Objective-C
MLKInk *ink = ...; MLKDigitalInkRecognizer *recognizer = ...; NSString *preContext = ...; MLKWritingArea *writingArea = [MLKWritingArea initWithWidth:... height:...]; MLKDigitalInkRecognitionContext *context = [MLKDigitalInkRecognitionContext initWithPreContext:preContext writingArea:writingArea]; [recognizer recognizeHandwritingFromInk:ink context:context completion:^(MLKDigitalInkRecognitionResult *_Nullable result, NSError *_Nullable error) { NSLog(@"Recognition result %@", result.candidates[0].text); }];
Strichreihenfolge
Die Genauigkeit der Erkennung hängt von der Reihenfolge der Striche ab. Die Erkennungsfunktionen erwarten, dass in der Reihenfolge, in der die Nutzer schreiben würden; z. B. von links nach rechts für Englisch. Beliebiger Fall zum Beispiel einen englischen Satz, der mit dem letzten Wort beginnt, führt zu weniger genauen Ergebnissen.
Ein weiteres Beispiel: Ein Wort mitten in einem Ink
wird entfernt und durch
ein anderes Wort. Die Überarbeitung steht wahrscheinlich mitten in einem Satz, aber die Überarbeitung
am Ende der Strichfolge stehen.
In diesem Fall empfehlen wir, das neu geschriebene Wort separat an die API zu senden und die
Ergebnis mit den vorherigen Erkennungen
unter Verwendung Ihrer eigenen Logik erhalten.
Umgang mit mehrdeutigen Formen
Es gibt Fälle, in denen die Bedeutung der dem Erkennungsmodul übergebenen Form mehrdeutig ist. Für Ein Rechteck mit stark abgerundeten Kanten kann entweder als Rechteck oder als Ellipse betrachtet werden.
Diese unklaren Fälle können mithilfe von Erkennungswerten bearbeitet werden, sofern sie verfügbar sind. Nur
Formklassifikatoren liefern Bewertungen. Wenn das Modell sehr sicher ist, ist das Top-Ergebnis
viel besser als die zweitbeste. Wenn es Unsicherheiten gibt, werden die Punkte für die beiden besten Ergebnisse
nah sein. Beachten Sie außerdem, dass die Formklassifikatoren das gesamte Ink
als
in eine einzelne Form ein. Wenn Ink
beispielsweise ein Rechteck und eine Ellipse daneben enthält,
kann das Erkennungsmodul das eine oder das andere (oder etwas völlig anderes) als
da ein Erkennungskandidat nicht zwei Formen darstellen kann.