Mit der digitalen Handschrifterkennung von ML Kit können Sie handschriftlichen Text auf einer digitalen Oberfläche in Hunderten von Sprachen erkennen und Skizzen klassifizieren.
Jetzt ausprobieren
- Probieren Sie die Beispiel-App aus, um ein Anwendungsbeispiel für diese API zu sehen.
Hinweis
- In die Datei
build.gradleauf Projektebene muss das Maven-Repository von Google in die Abschnittebuildscriptundallprojectsaufgenommen werden. - Fügen Sie die Abhängigkeiten für die ML Kit Android-Bibliotheken zur Gradle-Datei auf App-Ebene Ihres Moduls hinzu, die normalerweise
app/build.gradlelautet:
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:19.0.0'
}
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 einem Touchscreen zu zeichnen. Unter
Android können Sie dazu ein
Canvas verwenden. Ihre
Touch-Ereignis Handler
sollten die addNewTouchEvent()
Methode aufrufen, die im folgenden Code-Snippet gezeigt wird, um die Punkte in den Strichen, die der
Nutzer zeichnet, im Ink Objekt zu speichern.
Dieses allgemeine Muster wird im folgenden Code-Snippet veranschaulicht. Ein vollständigeres Beispiel finden Sie im ML Kit-Schnellstartbeispiel.
Kotlin
var inkBuilder = Ink.builder() lateinit var strokeBuilder: Ink.Stroke.Builder // Call this each time there is a new event. fun addNewTouchEvent(event: MotionEvent) { val action = event.actionMasked val x = event.x val y = event.y var t = System.currentTimeMillis() // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create when (action) { MotionEvent.ACTION_DOWN -> { strokeBuilder = Ink.Stroke.builder() strokeBuilder.addPoint(Ink.Point.create(x, y, t)) } MotionEvent.ACTION_MOVE -> strokeBuilder!!.addPoint(Ink.Point.create(x, y, t)) MotionEvent.ACTION_UP -> { strokeBuilder.addPoint(Ink.Point.create(x, y, t)) inkBuilder.addStroke(strokeBuilder.build()) } else -> { // Action not relevant for ink construction } } } ... // This is what to send to the recognizer. val ink = inkBuilder.build()
Java
Ink.Builder inkBuilder = Ink.builder(); Ink.Stroke.Builder strokeBuilder; // Call this each time there is a new event. public void addNewTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); long t = System.currentTimeMillis(); // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: strokeBuilder = Ink.Stroke.builder(); strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_MOVE: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_UP: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); inkBuilder.addStroke(strokeBuilder.build()); strokeBuilder = null; break; } } ... // This is what to send to the recognizer. Ink ink = inkBuilder.build();
Instanz von DigitalInkRecognizer abrufen
Um die Erkennung durchzuführen, senden Sie die Ink-Instanz an ein
DigitalInkRecognizer-Objekt. Der folgende Code zeigt, wie Sie einen solchen
Recognizer aus einem BCP-47 Tag instanziieren.
Kotlin
// Specify the recognition model for a language var modelIdentifier: DigitalInkRecognitionModelIdentifier try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US") } catch (e: MlKitException) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } var model: DigitalInkRecognitionModel = DigitalInkRecognitionModel.builder(modelIdentifier).build() // Get a recognizer for the language var recognizer: DigitalInkRecognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build())
Java
// Specify the recognition model for a language DigitalInkRecognitionModelIdentifier modelIdentifier; try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US"); } catch (MlKitException e) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } DigitalInkRecognitionModel model = DigitalInkRecognitionModel.builder(modelIdentifier).build(); // Get a recognizer for the language DigitalInkRecognizer recognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build());
Ink-Objekt verarbeiten
Kotlin
recognizer.recognize(ink) .addOnSuccessListener { result: RecognitionResult -> // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. Log.i(TAG, result.candidates[0].text) } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error during recognition: $e") }
Java
recognizer.recognize(ink) .addOnSuccessListener( // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. result -> Log.i(TAG, result.getCandidates().get(0).getText())) .addOnFailureListener( e -> Log.e(TAG, "Error during recognition: " + e));
Im obigen Beispielcode wird davon ausgegangen, dass das Erkennungsmodell bereits heruntergeladen wurde, wie im nächsten Abschnitt beschrieben.
Modell-Downloads verwalten
Die Digital Ink Recognition API unterstützt Hunderte von Sprachen. Für jede Sprache müssen jedoch vor der Erkennung Daten heruntergeladen werden. Pro Sprache sind etwa 20 MB Speicherplatz erforderlich. Dies wird vom RemoteModelManager-Objekt verarbeitet.
Neues Modell herunterladen
Kotlin
import com.google.mlkit.common.model.DownloadConditions import com.google.mlkit.common.model.RemoteModelManager var model: DigitalInkRecognitionModel = ... val remoteModelManager = RemoteModelManager.getInstance() remoteModelManager.download(model, DownloadConditions.Builder().build()) .addOnSuccessListener { Log.i(TAG, "Model downloaded") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while downloading a model: $e") }
Java
import com.google.mlkit.common.model.DownloadConditions; import com.google.mlkit.common.model.RemoteModelManager; DigitalInkRecognitionModel model = ...; RemoteModelManager remoteModelManager = RemoteModelManager.getInstance(); remoteModelManager .download(model, new DownloadConditions.Builder().build()) .addOnSuccessListener(aVoid -> Log.i(TAG, "Model downloaded")) .addOnFailureListener( e -> Log.e(TAG, "Error while downloading a model: " + e));
Prüfen, ob ein Modell bereits heruntergeladen wurde
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
Heruntergeladenes Modell löschen
Wenn Sie ein Modell aus dem Speicher des Geräts entfernen, wird Speicherplatz freigegeben.
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener { Log.i(TAG, "Model successfully deleted") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while deleting a model: $e") }
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener( aVoid -> Log.i(TAG, "Model successfully deleted")) .addOnFailureListener( e -> Log.e(TAG, "Error while deleting a model: " + e));
Tipps zur Verbesserung der Genauigkeit der Texterkennung
Die Genauigkeit der Texterkennung kann je nach Sprache variieren. Die Genauigkeit hängt auch vom Schreibstil ab. Die Digital Ink Recognition ist für viele Arten von Schreibstilen trainiert, die Ergebnisse können jedoch je nach Nutzer variieren.
Hier sind einige Möglichkeiten, die Genauigkeit eines Texterkenners zu verbessern. Beachten Sie, dass diese Verfahren nicht für die Zeichenklassifikatoren für Emojis, Autodraw und Formen gelten.
Schreibfläche
Viele Anwendungen haben eine klar definierte Schreibfläche für die Nutzereingabe. Die Bedeutung eines Symbols wird teilweise durch seine Größe im Verhältnis zur Größe der Schreibfläche bestimmt, auf der es sich befindet. Beispiel: der Unterschied zwischen einem Klein- oder Großbuchstaben „o“ oder „c“ und einem Komma im Vergleich zu einem Schrägstrich.
Wenn Sie dem Recognizer die Breite und Höhe der Schreibfläche mitteilen, kann die Genauigkeit verbessert werden. Der Recognizer geht jedoch davon aus, dass die Schreibfläche nur eine Textzeile enthält. Wenn die physische Schreibfläche groß genug ist, damit der Nutzer zwei oder mehr Zeilen schreiben kann, erzielen Sie möglicherweise bessere Ergebnisse, wenn Sie eine WritingArea mit einer Höhe übergeben, die Ihrer besten Schätzung der Höhe einer einzelnen Textzeile entspricht. Das WritingArea-Objekt, das Sie an den Recognizer übergeben, muss nicht genau der physischen Schreibfläche auf dem Bildschirm entsprechen. Die Änderung der WritingArea-Höhe auf diese Weise funktioniert in einigen Sprachen besser als in anderen.
Wenn Sie die Schreibfläche angeben, geben Sie ihre Breite und Höhe in denselben Einheiten wie die Strichkoordinaten an. Für die Argumente der x‑ und y‑Koordinaten ist keine Einheit erforderlich. Die API normalisiert alle Einheiten. Es kommt also nur auf die relative Größe und Position der Striche an. Sie können Koordinaten in einer beliebigen Skala übergeben, die für Ihr System sinnvoll ist.
Vorkontext
Der Vorkontext ist der Text, der den Strichen im Ink unmittelbar vorangeht, die Sie erkennen möchten. Sie können dem Recognizer helfen, indem Sie ihm den Vorkontext mitteilen.
Beispielsweise werden die kursiven Buchstaben „n“ und „u“ oft verwechselt. Wenn der Nutzer bereits das teilweise Wort „arg“ eingegeben hat, fährt er möglicherweise mit Strichen fort, die als „ument“ oder „nment“ erkannt werden können. Wenn Sie den Vorkontext „arg“ angeben, wird die Mehrdeutigkeit aufgelöst, da das Wort „argument“ wahrscheinlicher ist als „argnment“.
Der Vorkontext kann dem Recognizer auch helfen, Wortumbrüche zu erkennen, also die Leerzeichen zwischen Wörtern. Sie können ein Leerzeichen eingeben, aber nicht zeichnen. Wie kann ein Recognizer also feststellen, wann ein Wort endet und das nächste beginnt? Wenn der Nutzer bereits „hallo“ geschrieben hat und mit dem geschriebenen Wort „Welt“ fortfährt, gibt der Recognizer ohne Vorkontext den String „Welt“ zurück. Wenn Sie jedoch den Vorkontext „hallo“ angeben, gibt das Modell den String „ Welt“ mit einem vorangestellten Leerzeichen zurück, da „hallo Welt“ sinnvoller ist als „halloWelt“.
Sie sollten den längstmöglichen Vorkontext-String angeben, bis zu 20 Zeichen, einschließlich Leerzeichen. Wenn der String länger ist, verwendet der Recognizer nur die letzten 20 Zeichen.
Das folgende Codebeispiel zeigt, wie Sie eine Schreibfläche definieren und ein RecognitionContext-Objekt verwenden, um den Vorkontext anzugeben.
Kotlin
var preContext : String = ...; var width : Float = ...; var height : Float = ...; val recognitionContext : RecognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(WritingArea(width, height)) .build() recognizer.recognize(ink, recognitionContext)
Java
String preContext = ...; float width = ...; float height = ...; RecognitionContext recognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(new WritingArea(width, height)) .build(); recognizer.recognize(ink, recognitionContext);
Strichreihenfolge
Die Erkennungsgenauigkeit hängt von der Reihenfolge der Striche ab. Die Recognizer erwarten, dass die Striche in der Reihenfolge auftreten, in der Menschen normalerweise schreiben, z. B. von links nach rechts für Deutsch. Jeder Fall, der von diesem Muster abweicht, z. B. wenn Sie einen deutschen Satz schreiben, der mit dem letzten Wort beginnt, führt zu weniger genauen Ergebnissen.
Ein weiteres Beispiel ist, wenn ein Wort in der Mitte eines Ink entfernt und durch ein anderes Wort ersetzt wird. Die Überarbeitung befindet sich wahrscheinlich in der Mitte eines Satzes, die Striche für die Überarbeitung befinden sich jedoch am Ende der Strichfolge.
In diesem Fall empfehlen wir, das neu geschriebene Wort separat an die API zu senden und das Ergebnis mit Ihrer eigenen Logik mit den vorherigen Erkennungen zusammenzuführen.
Umgang mit mehrdeutigen Formen
Es gibt Fälle, in denen die Bedeutung der Form, die dem Recognizer zur Verfügung gestellt wird, mehrdeutig ist. Ein Rechteck mit sehr abgerundeten Ecken kann beispielsweise entweder als Rechteck oder als Ellipse angesehen werden.
Diese unklaren Fälle können mithilfe von Erkennungswerten behandelt werden, wenn sie verfügbar sind. Nur Formenklassifizierer liefern Werte. Wenn das Modell sehr zuversichtlich ist, ist der Wert des besten Ergebnisses viel besser als der des zweitbesten. Bei Unsicherheit sind die Werte für die beiden besten Ergebnisse ähnlich. Beachten Sie außerdem, dass die Formenklassifizierer das gesamte Ink als eine einzelne Form interpretieren. Wenn das Ink beispielsweise ein Rechteck und eine Ellipse nebeneinander enthält, kann der Recognizer eines der beiden (oder etwas ganz anderes) als Ergebnis zurückgeben, da ein einzelner Erkennungskandidat nicht zwei Formen darstellen kann.