Handschrifterkennung von Nutzern

Mit der Handschrifterkennungs-API können Sie handschriftlichen Text sofort erkennen.

Was ist die Handschrifterkennungs-API?

Mit der Handschrifterkennungs-API können Sie Handschrift (Tinte) Ihrer Nutzer in Text umwandeln. Einige Betriebssysteme enthalten seit Langem solche APIs. Mit dieser neuen Funktion können Ihre Webanwendungen diese Funktion endlich nutzen. Die Konvertierung erfolgt direkt auf dem Gerät des Nutzers, funktioniert sogar im Offlinemodus, ohne dass Bibliotheken oder Dienste von Drittanbietern hinzugefügt werden müssen.

Diese API implementiert eine sogenannte Online- oder echtzeitnahe Erkennung. Das bedeutet, dass die handschriftlichen Eingabe erkannt wird, während der Nutzer sie zeichnet, indem die einzelnen Striche erfasst und analysiert werden. Im Gegensatz zu Offline-Verfahren wie der optischen Zeichenerkennung (Optical Character Recognition, OCR), bei denen nur das Endprodukt bekannt ist, können Online-Algorithmen aufgrund zusätzlicher Signale wie der zeitlichen Abfolge und des Drucks einzelner Tintenstriche ein höheres Genauigkeitsniveau bieten.

Empfohlene Anwendungsfälle für die Handschrifterkennungs-API

Anwendungsbeispiele:

  • Notizenanwendungen, in denen Nutzer handschriftliche Notizen erfassen und in Text übersetzen lassen möchten.
  • Formularanwendungen, bei denen Nutzer bei zeitlicher Beschränkung die Eingabe per Stift oder Finger verwenden können
  • Spiele, für die Buchstaben oder Zahlen eingegeben werden müssen, z. B. Kreuzworträtsel, Galgenmännchen oder Sudoku.

Aktueller Status

Die Handschrifterkennungs-API ist bei Chromium 99 erhältlich.

Handschrifterkennungs-API verwenden

Funktionserkennung

Prüfen Sie, ob die Methode createHandwritingRecognizer() im Navigatorobjekt vorhanden ist, um die Browserunterstützung zu ermitteln:

if ('createHandwritingRecognizer' in navigator) {
  // 🎉 The Handwriting Recognition API is supported!
}

Wichtige Konzepte

Die Handschrifterkennungs-API wandelt handschriftliche Eingaben unabhängig von der Eingabemethode (Maus, Berührung, Stift) in Text um. Die API hat vier Hauptentitäten:

  1. Ein Punkt gibt an, wo sich der Mauszeiger zu einem bestimmten Zeitpunkt befand.
  2. Ein Strich besteht aus einem oder mehreren Punkten. Die Aufzeichnung eines Strichs beginnt, wenn der Nutzer den Zeiger nach unten setzt (d.h. auf die primäre Maustaste klickt oder den Bildschirm mit einem Stift oder Finger berührt) und endet, wenn er ihn wieder nach oben hebt.
  3. Eine Zeichnung besteht aus einer oder mehreren Strichen. Die eigentliche Erkennung findet auf dieser Ebene statt.
  4. Die Erkennung ist mit der erwarteten Eingabesprache konfiguriert. Damit wird eine Instanz einer Zeichnung mit der Konfiguration der Erkennung erstellt.

Diese Konzepte werden als spezifische Schnittstellen und Wörterbücher implementiert, die ich in Kürze behandeln werde.

Die Kernelemente der Handschrifterkennungs-API: Aus einem oder mehreren Punkten ergibt sich ein Strich, aus einem oder mehreren Strichen wird eine Zeichnung, die mit der Erkennung erstellt wird. Die eigentliche Erkennung erfolgt auf Zeichenebene.

Erkennung erstellen

Um Text aus handschriftlichen Eingaben zu erkennen, müssen Sie eine Instanz eines HandwritingRecognizer abrufen. Rufen Sie dazu navigator.createHandwritingRecognizer() auf und übergeben Sie Einschränkungen. Einschränkungen bestimmen, welches Modell zur Handschrifterkennung verwendet werden sollte. Derzeit können Sie eine Liste von Sprachen in der gewünschten Reihenfolge angeben:

const recognizer = await navigator.createHandwritingRecognizer({
  languages: ['en'],
});

Die Methode gibt ein Versprechen zurück, das mit einer Instanz von HandwritingRecognizer aufgelöst wird, wenn der Browser Ihre Anfrage ausführen kann. Andernfalls wird das Versprechen mit einem Fehler abgelehnt und die Handschrifterkennung ist nicht verfügbar. Aus diesem Grund sollten Sie die Unterstützung der Erkennung für bestimmte Erkennungsfunktionen zuerst abfragen.

Unterstützung der Erkennung abfragen

Durch Aufrufen von navigator.queryHandwritingRecognizerSupport() können Sie prüfen, ob die Zielplattform die Features zur Handschrifterkennung unterstützt, die Sie verwenden möchten. Im folgenden Beispiel geht der Entwickler so vor:

  • möchte Texte auf Englisch erkennen
  • alternative, weniger wahrscheinliche Vorhersagen erhalten, falls verfügbar
  • Zugriff auf das Segmentierungsergebnis erhalten, d.h. die erkannten Zeichen, einschließlich der Punkte und Striche, aus denen sie bestehen
const { languages, alternatives, segmentationResults } =
  await navigator.queryHandwritingRecognizerSupport({
    languages: ['en'],
    alternatives: true,
    segmentationResult: true,
  });

console.log(languages); // true or false
console.log(alternatives); // true or false
console.log(segmentationResult); // true or false

Die Methode gibt ein Versprechen zurück, das mit einem Ergebnisobjekt aufgelöst wird. Wenn der Browser die vom Entwickler angegebene Funktion unterstützt, wird der Wert auf true festgelegt. Andernfalls wird er auf false festgelegt. Sie können diese Informationen verwenden, um bestimmte Features in Ihrer Anwendung zu aktivieren oder zu deaktivieren oder um Ihre Abfrage anzupassen und eine neue zu senden.

Zeichnung beginnen

Innerhalb Ihrer Anwendung sollten Sie einen Eingabebereich anbieten, in dem Nutzer handschriftliche Einträge eingeben können. Aus Leistungsgründen wird empfohlen, dies mithilfe eines Canvas-Objekts zu implementieren. Auf die genaue Implementierung dieses Teils wird in diesem Artikel nicht eingegangen. Wie das geht, erfahren Sie in der Demo.

Um eine neue Zeichnung zu starten, rufen Sie die Methode startDrawing() für die Erkennung auf. Bei dieser Methode wird ein Objekt mit verschiedenen Hinweisen verwendet, um den Erkennungsalgorithmus zu optimieren. Alle Hinweise sind optional:

  • Die Art des eingegebenen Texts: Text, E-Mail-Adressen, Zahlen oder ein einzelnes Zeichen (recognitionType)
  • Der Typ des Eingabegeräts: Maus-, Touch- oder Stifteingabe (inputType)
  • Der vorherige Text (textContext)
  • Die Anzahl der weniger wahrscheinlichen alternativen Vorhersagen, die zurückgegeben werden sollen (alternatives)
  • Eine Liste mit vom Nutzer identifizierbaren Zeichen („Graphemes“), die der Nutzer höchstwahrscheinlich eingibt (graphemeSet).

Die Handschrifterkennungs-API funktioniert gut mit Zeigerereignissen, die eine abstrakte Schnittstelle zur Verarbeitung von Eingaben von jedem Zeigegerät bieten. Die Argumente des Zeigerereignisses enthalten den verwendeten Zeigertyp. Das bedeutet, dass Sie Zeigerereignisse verwenden können, um den Eingabetyp automatisch zu bestimmen. Im folgenden Beispiel wird die Zeichnung zur Handschrifterkennung automatisch beim ersten Auftreten eines pointerdown-Ereignisses im Handschriftbereich erstellt. Da pointerType leer oder auf einen proprietären Wert festgelegt sein kann, habe ich eine Konsistenzprüfung eingeführt, um sicherzustellen, dass nur unterstützte Werte für den Eingabetyp der Zeichnung festgelegt sind.

let drawing;
let activeStroke;

canvas.addEventListener('pointerdown', (event) => {
  if (!drawing) {
    drawing = recognizer.startDrawing({
      recognitionType: 'text', // email, number, per-character
      inputType: ['mouse', 'touch', 'pen'].find((type) => type === event.pointerType),
      textContext: 'Hello, ',
      alternatives: 2,
      graphemeSet: ['f', 'i', 'z', 'b', 'u'], // for a fizz buzz entry form
    });
  }
  startStroke(event);
});

Schwimmstil hinzufügen

Das pointerdown-Ereignis ist auch der richtige Ort, um einen neuen Strich zu beginnen. Erstellen Sie dazu eine neue Instanz von HandwritingStroke. Außerdem sollten Sie die aktuelle Zeit als Bezugspunkt für die nachfolgenden Punkte speichern, die ihr hinzugefügt werden:

function startStroke(event) {
  activeStroke = {
    stroke: new HandwritingStroke(),
    startTime: Date.now(),
  };
  addPoint(event);
}

Punkt hinzufügen

Nachdem Sie die Kontur erstellt haben, sollten Sie den ersten Punkt direkt hinzufügen. Da Sie später weitere Punkte hinzufügen, ist es sinnvoll, die Logik zur Punkterstellung in einer separaten Methode zu implementieren. Im folgenden Beispiel berechnet die Methode addPoint() die verstrichene Zeit ab dem Referenzzeitstempel. Die zeitlichen Informationen sind optional, können aber die Erkennungsqualität verbessern. Anschließend werden die X- und Y-Koordinaten aus dem Zeigerereignis ausgelesen und der Punkt dem aktuellen Strich hinzugefügt.

function addPoint(event) {
  const timeElapsed = Date.now() - activeStroke.startTime;
  activeStroke.stroke.addPoint({
    x: event.offsetX,
    y: event.offsetY,
    t: timeElapsed,
  });
}

Der pointermove-Event-Handler wird aufgerufen, wenn der Mauszeiger über den Bildschirm bewegt wird. Diese Punkte müssen ebenfalls dem Strich hinzugefügt werden. Das Ereignis kann auch ausgelöst werden, wenn sich der Zeiger nicht in einem "Abwärts"-Zustand befindet, z. B. wenn der Cursor über den Bildschirm bewegt wird, ohne die Maustaste zu drücken. Der Event-Handler aus dem folgenden Beispiel prüft, ob ein aktiver Strich vorhanden ist, und fügt ihm den neuen Punkt hinzu.

canvas.addEventListener('pointermove', (event) => {
  if (activeStroke) {
    addPoint(event);
  }
});

Text erkennen

Wenn der Nutzer den Mauszeiger wieder anhebt, können Sie die Kontur durch Aufrufen der addStroke()-Methode Ihrer Zeichnung hinzufügen. Im folgenden Beispiel wird auch activeStroke zurückgesetzt, sodass der pointermove-Handler dem abgeschlossenen Strich keine Punkte hinzufügt.

Als Nächstes wird die Eingabe des Nutzers durch Aufrufen der Methode getPrediction() für die Zeichnung erkannt. Die Erkennung dauert in der Regel weniger als ein paar Hundert Millisekunden, sodass Sie bei Bedarf wiederholt Vorhersagen ausführen können. Im folgenden Beispiel wird nach jedem abgeschlossenen Strich eine neue Vorhersage ausgeführt.

canvas.addEventListener('pointerup', async (event) => {
  drawing.addStroke(activeStroke.stroke);
  activeStroke = null;

  const [mostLikelyPrediction, ...lessLikelyAlternatives] = await drawing.getPrediction();
  if (mostLikelyPrediction) {
    console.log(mostLikelyPrediction.text);
  }
  lessLikelyAlternatives?.forEach((alternative) => console.log(alternative.text));
});

Diese Methode gibt ein Versprechen zurück, das mit einem Array von Vorhersagen, sortiert nach ihrer Wahrscheinlichkeit, aufgelöst wird. Die Anzahl der Elemente hängt von dem Wert ab, den Sie an den alternatives-Hinweis übergeben haben. Mit diesem Array können Sie dem Nutzer eine Auswahl möglicher Übereinstimmungen anzeigen und ihn eine Option auswählen lassen. Alternativ können Sie einfach die wahrscheinlichste Vorhersage verwenden, wie ich es im Beispiel mache.

Das Vorhersageobjekt enthält den erkannten Text und ein optionales Segmentierungsergebnis, auf das ich im folgenden Abschnitt näher eingehen werde.

Detaillierte Statistiken mit Segmentierungsergebnissen

Sofern dies von der Zielplattform unterstützt wird, kann das Vorhersageobjekt auch ein Segmentierungsergebnis enthalten. Dies ist ein Array, das alle Segmente mit erkannten Handschriften, eine Kombination aus dem erkannten, vom Nutzer identifizierbaren Zeichen (grapheme), seiner Position im erkannten Text (beginIndex, endIndex) und den Strichen und Punkten enthält, die es erstellt haben.

if (mostLikelyPrediction.segmentationResult) {
  mostLikelyPrediction.segmentationResult.forEach(
    ({ grapheme, beginIndex, endIndex, drawingSegments }) => {
      console.log(grapheme, beginIndex, endIndex);
      drawingSegments.forEach(({ strokeIndex, beginPointIndex, endPointIndex }) => {
        console.log(strokeIndex, beginPointIndex, endPointIndex);
      });
    },
  );
}

Anhand dieser Informationen können Sie die erkannten Grapheme auf dem Canvas noch einmal aufspüren.

Felder werden um jeden erkannten Graphem gezeichnet

Vollständige Erkennung

Wenn die Erkennung abgeschlossen ist, können Sie Ressourcen freigeben, indem Sie die Methode clear() für HandwritingDrawing und die Methode finish() für HandwritingRecognizer aufrufen:

drawing.clear();
recognizer.finish();

Demo

Die Webkomponente <handwriting-textarea> implementiert eine schrittweise verbesserte Bearbeitungssteuerung, die Handschrifterkennung unterstützt. Wenn Sie auf die Schaltfläche rechts unten im Bearbeitungssteuerelement klicken, wird der Zeichenmodus aktiviert. Wenn Sie die Zeichnung fertiggestellt haben, startet die Webkomponente die Erkennung automatisch und fügt den erkannten Text wieder dem Bearbeitungssteuerelement hinzu. Wenn die Handschrifterkennungs-API überhaupt nicht unterstützt wird oder die Plattform die angeforderten Funktionen nicht unterstützt, wird die Schaltfläche „Bearbeiten“ ausgeblendet. Das grundlegende Bearbeitungssteuerelement kann jedoch als <textarea> verwendet werden.

Die Webkomponente bietet Eigenschaften und Attribute, mit denen das Erkennungsverhalten von außen definiert werden kann, einschließlich languages und recognitiontype. Sie können den Inhalt des Steuerelements über das Attribut value festlegen:

<handwriting-textarea languages="en" recognitiontype="text" value="Hello"></handwriting-textarea>

Wenn Sie über Änderungen am Wert informiert werden möchten, können Sie auf das Ereignis input warten.

Du kannst die Komponente mit dieser Demo auf Glitch ausprobieren. Wir empfehlen Ihnen außerdem, einen Blick auf den Quellcode zu werfen. Wenn Sie das Steuerelement in Ihrer Anwendung verwenden möchten, rufen Sie es von npm ab.

Sicherheit und Berechtigungen

Das Chromium-Team hat die Handschrifterkennungs-API gemäß den Grundprinzipien entwickelt und implementiert, die unter Zugriff auf leistungsstarke Webplattformfunktionen steuern definiert wurden, darunter Nutzersteuerung, Transparenz und Ergonomie.

Nutzersteuerung

Die Handschrifterkennungs-API kann vom Nutzer nicht deaktiviert werden. Sie ist nur für Websites verfügbar, die über HTTPS bereitgestellt werden, und kann nur aus dem Browser-Kontext der obersten Ebene aufgerufen werden.

Transparenz

Es wird nicht angezeigt, ob die Handschrifterkennung aktiv ist. Um Fingerprinting zu verhindern, implementiert der Browser Gegenmaßnahmen, z. B. wird dem Nutzer eine Berechtigungsaufforderung angezeigt, wenn er einen möglichen Missbrauch erkennt.

Berechtigungspersistenz

Die Handwriting Recognition API zeigt derzeit keine Aufforderungen zu Berechtigungen an. Daher muss die Berechtigung in keiner Weise beibehalten werden.

Feedback

Das Chromium-Team möchte mehr über Ihre Erfahrungen mit der Handschrifterkennungs-API erfahren.

Informationen zum API-Design

Gibt es etwas an der API, das nicht wie erwartet funktioniert? Oder fehlen Methoden oder Eigenschaften, um Ihre Idee umzusetzen? Haben Sie eine Frage oder einen Kommentar zum Sicherheitsmodell? Sie können ein Spezifikationsproblem im entsprechenden GitHub-Repository melden oder Ihre Gedanken zu einem vorhandenen Problem hinzufügen.

Problem mit der Implementierung melden

Haben Sie bei der Implementierung von Chromium einen Fehler gefunden? Oder unterscheidet sich die Implementierung von der Spezifikation? Reichen Sie einen Fehler unter new.crbug.com ein. Geben Sie dabei so viele Details wie möglich und eine einfache Anleitung zum Reproduzieren an. Geben Sie Blink>Handwriting in das Feld Komponenten ein. Mit Glitch lassen sich schnell und einfach Reproduktionen durchführen.

Unterstützung für die API zeigen

Möchten Sie die Handschrifterkennungs-API verwenden? Deine öffentliche Unterstützung hilft dem Chromium-Team, Funktionen zu priorisieren und anderen Browseranbietern zu zeigen, wie wichtig es ist, sie zu unterstützen.

Teile über die geplante Verwendung im WICG-Discourse-Thread mit. Sende einen Tweet an @ChromiumDev mit dem Hashtag #HandwritingRecognition und teile uns mit, wo und wie du es verwendest.

Danksagungen

Dieser Artikel wurde von Joe Medley, Honglin Yu und Jiewei Qian geprüft. Hero-Image von Samir Bouaked auf Unsplash