Digitale Tinte mit ML Kit unter Android erkennen

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.

<ph type="x-smartling-placeholder">

Jetzt ausprobieren

  • Probieren Sie die Beispiel-App aus, um sehen Sie sich ein Anwendungsbeispiel für diese API an.

Hinweis

<ph type="x-smartling-placeholder">
  1. Fügen Sie in der Datei build.gradle auf Projektebene das Maven-Repository von Google in die Abschnitte buildscript und allprojects ein.
  2. Fügen Sie die Abhängigkeiten für die ML Kit-Android-Bibliotheken in die Gradle-Datei auf App-Ebene Ihres Moduls ein, die normalerweise app/build.gradle ist:
dependencies {
  // ...
  implementation 'com.google.mlkit:digital-ink-recognition:18.1.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 einen Touchscreen zu zeichnen. An Android verwendet, können Sie Canvas für für diesen Zweck. Ihr touch event-Handler sollte addNewTouchEvent() aufrufen das folgende Code-Snippet gezeigt, um die Punkte in den Strichen der Nutzer in das Ink-Objekt einträgt.

Dieses allgemeine Muster wird im folgenden Code-Snippet demonstriert. Weitere Informationen finden Sie in der ML Kit-Schnellstartbeispiel finden Sie ein vollständigeres Beispiel.

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 DigitalInkDetectr abrufen

Senden Sie für eine Erkennung die Instanz Ink an einen DigitalInkRecognizer-Objekt. Der folgende Code zeigt, wie eine solche BCP-47-Tag erkannt 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 wie im nächsten Abschnitt beschrieben.

Modelldownloads verwalten

Die Digital Ink Detection API unterstützt Hunderte von Sprachen, Sprache erfordert, dass einige Daten vor der Erkennung heruntergeladen werden. Ungefähr Pro Sprache sind 20 MB Speicherplatz erforderlich. Dies wird vom RemoteModelManager-Objekt.

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 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.

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 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 stehen am Ende der Strichfolge. 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.