Mit der Handschrifterkennung von ML Kit können Sie handgeschriebenen Text auf einer digitalen Oberfläche in Hunderten von Sprachen erkennen und Skizzen klassifizieren.
Jetzt ausprobieren
- Beispiel-App ausprobieren, um ein Beispiel für die Verwendung dieser API zu sehen.
Hinweis
- In die Datei
build.gradle
auf Projektebene muss das Maven-Repository von Google in die Abschnittebuildscript
undallprojects
aufgenommen werden. - Fügen Sie der Gradle-Datei Ihres Moduls auf App-Ebene, in der Regel
app/build.gradle
, die Abhängigkeiten für die ML Kit Android-Bibliotheken hinzu:
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 wichtigste Methode zum Erstellen eines Ink
-Objekts ist das Zeichnen auf einem Touchscreen. Auf Android können Sie dazu ein Canvas verwenden. Ihre Touch-Ereignis-Handler sollten die im folgenden Code-Snippet gezeigte Methode addNewTouchEvent()
aufrufen, 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
Senden Sie zum Ausführen der Spracherkennung die Ink
-Instanz an ein DigitalInkRecognizer
-Objekt. Im folgenden Code wird gezeigt, wie ein solcher Erkennungsdienst aus einem BCP-47-Tag instanziiert wird.
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 einige Daten heruntergeladen werden. Pro Sprache sind etwa 20 MB Speicherplatz erforderlich. Dies wird vom RemoteModelManager
-Objekt übernommen.
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 Gerätespeicher 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 Handschrifterkennung ist zwar darauf ausgelegt, viele Arten von Schreibstilen zu verarbeiten, die Ergebnisse können jedoch von Nutzer zu Nutzer variieren.
Hier sind einige Möglichkeiten, die Genauigkeit eines Texterkenners zu verbessern. Diese Techniken werden nicht auf die Zeichenklassifizierer für Emojis, AutoDraw und Formen angewendet.
Schreibbereich
Viele Anwendungen haben einen klar definierten Schreibbereich für Nutzereingaben. Die Bedeutung eines Symbols wird teilweise durch seine Größe im Verhältnis zur Größe des Schreibbereichs bestimmt, in dem es enthalten ist. Beispiel: Der Unterschied zwischen einem Klein- oder Großbuchstaben „o“ oder „c“ und einem Komma im Vergleich zu einem Schrägstrich.
Wenn Sie dem Erkennungstool die Breite und Höhe des Schreibbereichs mitteilen, kann die Genauigkeit verbessert werden. Der Erkennungsdienst geht jedoch davon aus, dass der Schreibbereich nur eine Textzeile enthält. Wenn der physische Schreibbereich groß genug ist, damit der Nutzer zwei oder mehr Zeilen schreiben kann, erhalten Sie möglicherweise bessere Ergebnisse, wenn Sie einen „WritingArea“-Parameter mit einer Höhe übergeben, die Ihrer besten Schätzung der Höhe einer einzelnen Textzeile entspricht. Das WritingArea-Objekt, das Sie an die Erkennung übergeben, muss nicht genau mit dem physischen Schreibbereich auf dem Bildschirm übereinstimmen. Das Ändern der Höhe des Eingabebereichs auf diese Weise funktioniert in einigen Sprachen besser als in anderen.
Wenn Sie den Schreibbereich angeben, müssen Sie seine Breite und Höhe in denselben Einheiten wie die Strichkoordinaten angeben. Für die x,y-Koordinatenargumente 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 jeder Skala übergeben, die für Ihr System sinnvoll ist.
Vorkontext
Der Pre-Context ist der Text, der den Strichen in Ink
unmittelbar vorangeht und den Sie erkennen möchten. Sie können die Spracherkennung unterstützen, indem Sie ihr den Kontext vor dem eigentlichen Befehl mitteilen.
So werden beispielsweise die kursiven Buchstaben „n“ und „u“ oft verwechselt. Wenn der Nutzer bereits das Teilwort „arg“ eingegeben hat, kann er mit Strichen fortfahren, die als „ument“ oder „nment“ erkannt werden können. Durch die Angabe des Pre-Context „arg“ wird die Mehrdeutigkeit behoben, da das Wort „argument“ wahrscheinlicher ist als „argnment“.
Der Vorabkontext kann dem Erkennungsmodul auch dabei helfen, Worttrennungen, also die Leerzeichen zwischen Wörtern, zu erkennen. Sie können ein Leerzeichen eingeben, aber nicht zeichnen. Wie kann ein Erkennungsprogramm also feststellen, wann ein Wort endet und das nächste beginnt? Wenn der Nutzer bereits „Hallo“ geschrieben hat und mit dem Wort „Welt“ fortfährt, gibt die Spracherkennung ohne vorherigen Kontext den String „Welt“ zurück. Wenn Sie jedoch den Pre-Context „hello“ angeben, gibt das Modell den String „ world“ mit einem vorangestellten Leerzeichen zurück, da „hello world“ sinnvoller ist als „helloword“.
Geben Sie den längstmöglichen Pre-Context-String an, der bis zu 20 Zeichen lang sein darf, einschließlich Leerzeichen. Wenn der String länger ist, verwendet der Erkennungsdienst nur die letzten 20 Zeichen.
Im folgenden Codebeispiel wird gezeigt, wie Sie einen Schreibbereich 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);
Reihenfolge der Striche
Die Erkennungsgenauigkeit hängt von der Reihenfolge der Striche ab. Die Erkennungsfunktionen erwarten, dass Striche in der Reihenfolge ausgeführt werden, in der Menschen normalerweise schreiben, z. B. von links nach rechts für Englisch. Bei Abweichungen von diesem Muster, z. B. wenn Sie einen englischen Satz schreiben, der mit dem letzten Wort beginnt, sind die Ergebnisse weniger genau.
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 den vorherigen Erkennungen zusammenzuführen.
Umgang mit mehrdeutigen Formen
Es gibt Fälle, in denen die Bedeutung der Form, die dem Erkennungstool zur Verfügung gestellt wird, mehrdeutig ist. Ein Rechteck mit sehr abgerundeten Ecken kann beispielsweise entweder als Rechteck oder als Ellipse interpretiert werden.
Diese unklaren Fälle können mithilfe von Erkennungsergebnissen behandelt werden, sofern diese verfügbar sind. Nur Formklassifizierer 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. Außerdem interpretieren die Formklassifizierer das gesamte Ink
als eine einzelne Form. Wenn das Ink
beispielsweise ein Rechteck und eine Ellipse nebeneinander enthält, kann die Erkennung entweder das eine oder das andere (oder etwas ganz anderes) als Ergebnis zurückgeben, da ein einzelner Erkennungskandidat nicht zwei Formen darstellen kann.