Handschrifterkennung von Nutzern

Mit der Handschrifterkennungs-API können Sie Text aus handschriftlicher Eingabe in Echtzeit erkennen.

Was ist die Handwriting Recognition API?

Mit der Handschrifterkennungs-API können Sie die Handschrift (Tinte) Ihrer Nutzer in Text umwandeln. Einige Betriebssysteme haben solche APIs schon lange. Mit dieser neuen Funktion können Ihre Web-Apps diese Funktion endlich nutzen. Die Conversion erfolgt direkt auf dem Gerät des Nutzers und funktioniert auch im Offlinemodus – ganz ohne Bibliotheken oder Dienste von Drittanbietern.

Diese API implementiert die sogenannte „Onlineerkennung“ oder die Erkennung in nahezu Echtzeit. Das bedeutet, dass die handschriftliche Eingabe erkannt wird, während der Nutzer sie schreibt, 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 dem Druck einzelner Tintenstriche eine höhere Genauigkeit bieten.

Empfohlene Anwendungsfälle für die Handwriting Recognition API

Beispiele:

  • Notiz-Apps, in denen Nutzer handschriftliche Notizen erstellen und in Text umwandeln lassen möchten
  • Formulare, in denen Nutzer aufgrund von Zeitdruck Eingaben per Stift oder Finger machen können
  • Spiele, bei denen Buchstaben oder Zahlen eingegeben werden müssen, z. B. Kreuzworträtsel, Galgenmännchen oder Sudoku.

Aktueller Status

Die Handschrifterkennungs-API ist ab Chromium 99 verfügbar.

Verwendung der Handschrifterkennung API

Funktionserkennung

Prüfe, ob der Browser unterstützt wird, indem du prüfst, ob die Methode createHandwritingRecognizer() im Navigator-Objekt vorhanden ist:

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

Wichtige Konzepte

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

  1. Ein Punkt gibt an, wo sich der Cursor zu einem bestimmten Zeitpunkt befand.
  2. Ein Strich besteht aus einem oder mehreren Punkten. Die Aufzeichnung eines Strichs beginnt, wenn der Nutzer den Zeiger auf das Display setzt (d. h. auf die primäre Maustaste klickt oder den Bildschirm mit dem Stift oder Finger berührt) und endet, wenn er den Zeiger wieder anhebt.
  3. Eine Zeichnung besteht aus einem oder mehreren Strichen. Die eigentliche Erkennung erfolgt auf dieser Ebene.
  4. Der Erknerner ist für die erwartete Eingabesprache konfiguriert. Damit wird eine Instanz einer Zeichnung mit der angewendeten Erkennerkonfiguration erstellt.

Diese Konzepte werden als bestimmte Schnittstellen und Wörterbücher implementiert, auf die ich gleich näher eingehe.

Die Hauptentitäten der Handschrifterkennungs-API: Aus einem oder mehreren Punkten besteht ein Strich, aus einem oder mehreren Strichen eine Zeichnung, die von der Erkennung erstellt wird. Die eigentliche Erkennung erfolgt auf Zeichnungsebene.

Erkennungssystem erstellen

Wenn Sie Text aus handschriftlicher Eingabe erkennen möchten, müssen Sie eine Instanz von HandwritingRecognizer abrufen. Rufen Sie dazu navigator.createHandwritingRecognizer() auf und übergeben Sie ihm Einschränkungen. Mit Einschränkungen wird das zu verwendende Modell für die Handschrifterkennung festgelegt. Derzeit können Sie eine Liste von Sprachen in der bevorzugten Reihenfolge angeben:

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

Die Methode gibt ein Promise zurück, das mit einer Instanz eines HandwritingRecognizer aufgelöst wird, wenn der Browser die Anfrage erfüllen kann. Andernfalls wird die Zusicherung mit einem Fehler abgelehnt und die Handschrifterkennung ist nicht verfügbar. Aus diesem Grund sollten Sie zuerst die Unterstützung der Erkennungsfunktion für bestimmte Erkennungsfunktionen abfragen.

Unterstützung der Erkennungserkennung wird abgefragt

Wenn Sie navigator.queryHandwritingRecognizerSupport() aufrufen, können Sie prüfen, ob die Zielplattform die von Ihnen beabsichtigten Funktionen zur Handschrifterkennung unterstützt. Im folgenden Beispiel hat der Entwickler Folgendes getan:

  • möchte Text auf Englisch erkennen
  • alternative, weniger wahrscheinliche Vorhersagen erhalten, sofern verfügbar
  • Zugriff auf das Segmentierungsergebnis erhalten, d. h. auf 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 gesetzt. Andernfalls wird er auf false gesetzt. Anhand dieser Informationen können Sie bestimmte Funktionen in Ihrer Anwendung aktivieren oder deaktivieren oder Ihre Abfrage anpassen und eine neue senden.

Zeichnen starten

In Ihrer App sollte ein Eingabebereich vorhanden sein, in dem Nutzer ihre handschriftlichen Einträge machen können. Aus Leistungsgründen empfiehlt es sich, dafür ein Canvas-Objekt zu verwenden. Die genaue Implementierung dieses Teils geht über den Rahmen dieses Artikels hinaus. In der Demo sehen Sie, wie das geht.

Wenn Sie eine neue Zeichnung starten möchten, 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 Textes: Text, E-Mail-Adressen, Zahlen oder ein einzelnes Zeichen (recognitionType)
  • Art des Eingabegeräts: Maus, Touchbedienung oder Eingabe per Stift (inputType)
  • Der vorherige Text (textContext)
  • Die Anzahl unwahrscheinlicher alternativer Vorhersagen, die zurückgegeben werden sollten (alternatives)
  • Eine Liste von Zeichen, die der Nutzer mit hoher Wahrscheinlichkeit eingeben wird (graphemeSet)

Die Handschrifterkennungs-API funktioniert gut mit Maus-Ereignissen, die eine abstrakte Schnittstelle zum Verbrauchen von Eingaben von beliebigen Eingabegeräten bieten. Die Argumente für das Zeigerereignis enthalten den verwendeten Zeigertyp. Sie können also den Eingabetyp automatisch anhand von Zeigerereignissen bestimmen. Im folgenden Beispiel wird die Zeichnung für die Handschrifterkennung automatisch beim ersten Auftreten eines pointerdown-Ereignisses im Handschriftbereich erstellt. Da pointerType leer sein 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);
});

Strich hinzufügen

Das Ereignis pointerdown ist auch der richtige Ort, um einen neuen Strich zu beginnen. Dazu erstellen Sie eine neue Instanz von HandwritingStroke. Außerdem sollten Sie die aktuelle Uhrzeit als Referenzpunkt für die nachfolgenden Punkte speichern:

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 wird mit der Methode addPoint() die verstrichene Zeit ab dem Referenzzeitstempel berechnet. Die Zeitangaben sind optional, können aber die Erkennungsqualität verbessern. Anschließend werden die X- und Y-Koordinaten aus dem Zeigerereignis gelesen 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 Ereignis-Handler pointermove wird aufgerufen, wenn der Cursor über den Bildschirm bewegt wird. Diese Punkte müssen dem Strich ebenfalls hinzugefügt werden. Das Ereignis kann auch ausgelöst werden, wenn sich der Cursor nicht im Status „gedrückt“ befindet, z. B. wenn Sie den Cursor über den Bildschirm bewegen, ohne die Maustaste zu drücken. Der Event-Handler im folgenden Beispiel prüft, ob ein aktiver Stroke vorhanden ist, und fügt ihm den neuen Punkt hinzu.

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

Text erkennen

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

Als Nächstes müssen Sie die Eingabe des Nutzers erkennen. Rufen Sie dazu die Methode getPrediction() für die Zeichnung auf. Die Erkennung dauert in der Regel weniger als ein paar hundert Millisekunden. Sie können also bei Bedarf wiederholt Vorhersagen ausführen. 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 ein Array von Vorhersagen nach Wahrscheinlichkeit auflöst. Die Anzahl der Elemente hängt vom Wert ab, den Sie an den alternatives-Hinweis übergeben haben. Sie können dieses Array verwenden, um dem Nutzer eine Auswahl möglicher Übereinstimmungen anzubieten und ihn eine Option auswählen zu lassen. Alternativ können Sie sich einfach für die wahrscheinlichste Vorhersage entscheiden. Das habe ich in diesem Beispiel getan.

Das Vorhersageobjekt enthält den erkannten Text und ein optionales Segmentierungsergebnis, das ich im folgenden Abschnitt erläutern werde.

Detaillierte Statistiken mit Segmentierungsergebnissen

Wenn von der Zielplattform unterstützt, kann das Vorhersageobjekt auch ein Segmentierungsergebnis enthalten. Dies ist ein Array, das alle erkannten Handschriftensegmente enthält, eine Kombination aus dem erkannten, vom Nutzer identifizierbaren Zeichen (grapheme) zusammen mit seiner Position im erkannten Text (beginIndex, endIndex) und den Strichen und Punkten, aus denen es besteht.

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 auf dem Canvas erkannten Graphen wiederfinden.

Um jedes erkannte Graphem wird ein Rahmen gezogen.

Vollständige Erkennung

Nach Abschluss der Erkennung können Sie Ressourcen freigeben, indem Sie die Methode clear() für die HandwritingDrawing und die Methode finish() für die HandwritingRecognizer aufrufen:

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

Demo

Die Webkomponente <handwriting-textarea> implementiert eine schrittweise verbesserte Bearbeitungssteuerung, die Handschrift erkennen kann. Klicken Sie auf die Schaltfläche rechts unten im Bearbeitungskontrollelement, um den Zeichenmodus zu aktivieren. Sobald Sie die Zeichnung fertiggestellt haben, startet die Webkomponente automatisch die Erkennung und fügt den erkannten Text wieder dem Bearbeitungskontrollelement hinzu. Wenn die Handschrifterkennungs-API nicht unterstützt wird oder die Plattform die angeforderten Funktionen nicht unterstützt, wird die Schaltfläche „Bearbeiten“ ausgeblendet. Die grundlegenden Bearbeitungssteuerelemente können jedoch weiterhin 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 input-Ereignis warten.

Sie können die Komponente mit dieser Demo auf Glitch ausprobieren. Sehen Sie sich auch den Quellcode an. Wenn Sie das Steuerelement in Ihrer Anwendung verwenden möchten, laden Sie es von npm herunter.

Sicherheit und Berechtigungen

Das Chromium-Team hat die Handschrifterkennungs-API unter Berücksichtigung der in Controlling Access to Powerful Web Platform Features (Zugriff auf leistungsstarke Funktionen der Webplattform steuern) definierten Grundprinzipien entwickelt und implementiert, einschließlich 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 ausgeliefert werden, und kann nur vom Browserkontext der obersten Ebene aufgerufen werden.

Transparenz

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

Berechtigungsspeicherung

Die Handschrifterkennungs-API zeigt derzeit keine Berechtigungsaufforderungen an. Die Berechtigung muss also nicht auf irgendeine Weise beibehalten werden.

Feedback

Das Chromium-Team möchte mehr über Ihre Erfahrungen mit der Handwriting Recognition API wissen.

Informationen zum API-Design

Funktioniert die API nicht wie erwartet? Oder fehlen Methoden oder Eigenschaften, die Sie zur Umsetzung Ihrer Idee benötigen? Haben Sie Fragen oder Kommentare zum Sicherheitsmodell? Reichen Sie ein Problem mit der Spezifikation im entsprechenden GitHub-Repository ein oder fügen Sie Ihre Gedanken zu einem vorhandenen Problem hinzu.

Problem mit der Implementierung melden

Haben Sie einen Fehler in der Chromium-Implementierung gefunden? Oder weicht die Implementierung von der Spezifikation ab? Melden Sie einen Fehler unter new.crbug.com. Geben Sie so viele Details wie möglich und eine einfache Anleitung für die Reproduktion an. Geben Sie dann Blink>Handwriting in das Feld Komponenten ein. Glitch eignet sich hervorragend, um schnell und einfach Reproduktionen zu teilen.

Unterstützung für die API anzeigen

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

Im WICG Discourse-Thread können Sie uns mitteilen, wie Sie den Dienst verwenden möchten. Senden Sie einen Tweet an @ChromiumDev mit dem Hashtag #HandwritingRecognition und teilen Sie uns mit, wo und wie Sie ihn verwenden.

Danksagungen

Dieses Dokument wurde von Joe Medley, Honglin Yu und Jiewei Qian geprüft.