Mit der digitalen Tintenerkennung von ML Kit können Sie handschriftlichen Text auf Hunderten von Sprachen auf einer digitalen Oberfläche erkennen und Skizzen klassifizieren.
Ausprobieren
- Probieren Sie die Beispiel-App aus, um sich ein Beispiel für die Verwendung dieser API anzusehen.
Hinweis
- Achten Sie darauf, dass Sie in der Datei
build.gradle
auf Projektebene das Maven-Repository von Google in die Abschnittebuildscript
undallprojects
aufnehmen. - Fügen Sie der Gradle-Datei auf Modulebene Ihres Moduls die Abhängigkeiten für die Android-Bibliotheken des ML Kits hinzu, normalerweise
app/build.gradle
:
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:18.1.0'
}
Sie können jetzt Text in Ink
-Objekten erkennen.
Ink
-Objekt erstellen
Die beste Methode zum Erstellen eines Ink
-Objekts ist das Zeichnen auf einem Touchscreen. Unter Android können Sie dafür einen Canvas verwenden. Ihre Touch-Ereignis-Handler sollten die folgende addNewTouchEvent()
-Methode aufrufen, um das folgende Code-Snippet zu speichern, um die Punkte in den Strichen zu speichern, die der Nutzer in das Ink
-Objekt zeichnet.
Dieses allgemeine Muster wird im folgenden Code-Snippet veranschaulicht. Ein ausführlicheres Beispiel finden Sie in der Kurzanleitung zu ML Kit.
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();
DigitalInkDetectr-Instanz abrufen
Senden Sie die Ink
-Instanz an ein DigitalInkRecognizer
-Objekt, um die Erkennung auszuführen. Der folgende Code zeigt, wie eine solche Erkennung 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 Beispielcode oben wird davon ausgegangen, dass das Erkennungsmodell bereits heruntergeladen wurde, wie im nächsten Abschnitt beschrieben.
Modelldownloads verwalten
Die API für digitale Tintenerkennung unterstützt Hunderte von Sprachen. Es müssen jedoch vor der Erkennung einige Sprachen 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 ist auch vom Schreibstil abhängig. Die digitale Tintenerkennung ist darauf trainiert, viele verschiedene Schreibstile zu verarbeiten. Die Ergebnisse können jedoch von Nutzer zu Nutzer variieren.
Im Folgenden finden Sie einige Möglichkeiten, wie die Genauigkeit einer Texterkennung verbessert werden kann. Diese Techniken gelten nicht für den Zeichenklassifikator für Emojis, Emojis und Autoform.
Schreibbereich
Viele Anwendungen haben einen genau definierten Schreibbereich für die Nutzereingabe. Die Bedeutung eines Symbols richtet sich nach seiner Größe im Verhältnis zur Größe des Schreibbereichs, in dem es enthalten ist. Hier wird z. B. der Unterschied zwischen einem Kleinbuchstaben oder einem Großbuchstaben „o“ oder „c“ und einem Komma im Vergleich zu einem Schrägstrich.
Wenn Sie dem Erkennungsgerät die Breite und Höhe des Schreibbereichs mitteilen, kann die Genauigkeit verbessert werden. Dabei wird jedoch davon ausgegangen, dass der Schreibbereich nur eine einzige 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 „WriteArea“-Wert mit einer Höhe übergeben, die Ihrer bestmöglichen Schätzung der Höhe einer einzelnen Textzeile entspricht. Das WriterArea-Objekt, das Sie an die Erkennung übergeben, muss nicht genau mit dem physischen Schreibbereich auf dem Bildschirm übereinstimmen. Wenn Sie die Höhe für „WriteArea“ ändern, funktioniert das in einigen Sprachen besser als in anderen.
Wenn Sie den Schreibbereich angeben, geben Sie seine Breite und Höhe in denselben Einheiten wie die Strichkoordinaten an. Für die Argumente der x- und y-Koordinate ist keine Einheitsanforderung erforderlich. Die API normalisiert alle Einheiten. Die einzige Rolle dabei ist die relative Größe und Position der Striche. Sie können Koordinaten in jeder für Ihr System sinnvollen Größe übergeben.
Kontext
Der Pre-Kontext ist der Text, der den Strichen in der Ink
unmittelbar vorausgeht, die Sie erkennen möchten. Sie können der Erkennung helfen, indem Sie sie über den Kontext informieren.
Beispielsweise werden die Schreibbuchstaben „n“ und „u“ oft falsch betrachtet. Wenn der Nutzer bereits das unvollständige Wort „arg“ eingegeben hat, kann er mit Strichen fortfahren, die als „ument“ oder „nation“ erkannt werden. Durch Angabe des Pre-Kontexts „arg“ wird die Mehrdeutigkeit gelöst, da das Wort „Argument“ wahrscheinlicher ist als „argnment“.
Über den Kontext kann der Identifizierer auch Zeilenumbrüche erkennen, die Abstände zwischen Wörtern. Sie können ein Leerzeichen eingeben, aber nicht zeichnen. Wie kann eine Erkennung bestimmen, wann ein Wort endet und das nächste beginnt? Wenn der Nutzer bereits „hello“ geschrieben hat und mit dem geschriebenen Wort „world“ ohne Kontext weitergeht, gibt das Erkennungssystem den String „world“ zurück. Wenn Sie jedoch „pre-kontext“ angeben, gibt das Modell den String „world“ mit einem führenden Leerzeichen zurück, da „hello world“ sinnvoller als „helloword“ ist.
Der längst mögliche String für den Pre-Kontext darf maximal 20 Zeichen lang sein. Wenn der String länger ist, verwendet das Erkennungssystem nur die letzten 20 Zeichen.
Das folgende Codebeispiel zeigt, wie ein Schreibbereich definiert und ein RecognitionContext
-Objekt verwendet wird, um den Kontext 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);
Strichanordnung
Die Erkennungsgenauigkeit ist abhängig von der Reihenfolge der Striche. Die Erkenner erwarten, dass Striche in der Reihenfolge auftreten, in der die Menschen natürlich schreiben würden, z. B. von links nach rechts für Englisch. Alle von diesem Muster abweichenden Groß-/Kleinschreibung, z. B. das Schreiben eines englischen Satzes mit dem letzten Wort, liefern weniger genaue Ergebnisse.
Ein weiteres Beispiel: Ein Wort in der Mitte eines Ink
-Objekts wird entfernt und durch ein anderes Wort ersetzt. 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 Erkennungsvorgängen mit Ihrer eigenen Logik zusammenzuführen.
Umgang mit mehrdeutigen Formen
Es gibt Fälle, in denen die Bedeutung der Form, die der Ersteller erhalten hat, zweideutig ist. Ein Rechteck mit sehr abgerundeten Kanten ist beispielsweise ein Rechteck oder eine Ellipse.
Diese unklaren Fälle können mithilfe von Erkennungswerten gehandhabt werden, sobald sie verfügbar sind. Nur Formklassifikatoren stellen Punktzahlen bereit. Wenn das Modell sehr sicher ist, ist der Wert des besten Ergebnisses viel besser als das zweitbeste. Wenn die Ungewissheit unklar ist, liegen die Ergebnisse für die ersten beiden Ergebnisse nahe beieinander. Beachten Sie auch, dass die Formklassifikatoren die gesamte Ink
als einzelne Form interpretieren. Wenn Ink
beispielsweise ein Rechteck und eine Ellipse nebeneinander enthalten, kann die Erkennung eine der beiden Optionen zurückgeben, da sie nicht zwei Formen darstellen können.