Handschrifterkennung von Nutzern

Mit der Handwriting Recognition API können Sie Text in handschriftlichen Eingaben sofort erkennen.

Was ist die Handwriting Recognition API?

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

Diese API implementiert die sogenannte Online-Erkennung bzw. Erkennung nahezu in Echtzeit. Das bedeutet, dass die handschriftliche 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 dem nur das Endprodukt bekannt ist, können Online-Algorithmen aufgrund zusätzlicher Signale wie der zeitlichen Abfolge und des Drucks der einzelnen Tintenstriche ein höheres Maß an Genauigkeit bieten.

Empfohlene Anwendungsfälle für die Handwriting Recognition API

Anwendungsbeispiele:

  • Notizenanwendungen, in denen Nutzer handschriftliche Notizen erfassen und in Text übersetzen lassen möchten.
  • Google Formulare-Anwendungen, bei denen Nutzer aus Zeitgründen Stift oder Finger eingeben können.
  • Spiele, bei denen Buchstaben oder Zahlen eingegeben werden müssen, z. B. Kreuzworträtsel, Galgenmännchen oder Sudoku.

Aktueller Status

Die Handwriting Recognition API ist bei Chromium 99 verfügbar.

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 Handwriting Recognition 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. Eine Strich besteht aus einem oder mehreren Punkten. Die Aufzeichnung eines Strichs beginnt, wenn der Nutzer den Zeiger nach unten hält, 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 nach oben hebt.
  3. Eine Zeichnung besteht aus einem oder mehreren Strichen. Die tatsächliche Erkennung findet auf dieser Ebene statt.
  4. Der Erkennungscode wurde mit der erwarteten Eingabesprache konfiguriert. Sie wird verwendet, um eine Instanz einer Zeichnung zu erstellen, auf die die Erkennungskonfiguration angewendet wurde.

Diese Konzepte werden als spezifische Schnittstellen und Wörterbücher implementiert, auf die ich gleich noch eingehen werde.

Die Kernentitäten der Handschrifterkennungs-API: Ein oder mehrere Punkte bilden einen Strich, mindestens ein Strich stellt eine Zeichnung dar, die vom Erkennungsmodul erstellt wird. Die eigentliche Erkennung erfolgt auf Zeichnungsebene.

Erkennung erstellen

Damit Sie Text aus handschriftlichen Eingaben erkennen können, müssen Sie eine Instanz einer HandwritingRecognizer abrufen. Dazu rufen Sie navigator.createHandwritingRecognizer() auf und übergeben Einschränkungen. Das zu verwendende Handschrifterkennungsmodell wird durch Einschränkungen bestimmt. 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 das Versprechen 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

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

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

Zeichnung starten

Innerhalb Ihrer Anwendung sollten Sie einen Eingabebereich anbieten, in dem Nutzer ihre handschriftlichen Einträge eingeben können. Aus Leistungsgründen empfiehlt es sich, dafür ein Canvas-Objekt zu verwenden. Auf die genaue Implementierung dieses Teils wird in diesem Artikel nicht eingegangen. Sie können sich jedoch die Demo ansehen.

Wenn Sie eine neue Zeichnung starten möchten, rufen Sie die Methode startDrawing() für die Erkennung auf. Diese Methode verwendet ein Objekt, das verschiedene Hinweise enthält, um den Erkennungsalgorithmus zu optimieren. Alle Hinweise sind optional:

  • Die Art des eingegebenen Textes: Text, E-Mail-Adressen, Zahlen oder ein einzelnes Zeichen (recognitionType)
  • Die Art des Eingabegeräts: Maus, Berührung oder Stift (inputType)
  • Der vorherige Text (textContext)
  • Die Anzahl unwahrscheinlicher alternativer Vorhersagen, die zurückgegeben werden sollten (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 der Eingabe 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 pointerdown-Ereignis 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);
});

Kontur 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 Zeit als Referenz 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 berechnet die Methode addPoint() die verstrichene Zeit anhand des Referenzzeitstempels. Die zeitlichen Informationen sind optional, können aber die Erkennungsqualität verbessern. Dann werden die X- und Y-Koordinaten aus dem Zeigerereignis gelesen und der Punkt wird 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 auch der Kontur hinzugefügt werden. Das Ereignis kann auch ausgelöst werden, wenn der Zeiger nicht nach unten bewegt wird, 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 Zeiger wieder anhebt, können Sie die Kontur durch Aufrufen der entsprechenden 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 muss die Eingabe des Nutzers erkannt werden. Dazu wird die Methode getPrediction() für die Zeichnung aufgerufen. Die Erkennung dauert in der Regel weniger als einige Hundert Millisekunden, sodass Sie Vorhersagen bei Bedarf wiederholt 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 aufgelöst wird, das nach ihrer Wahrscheinlichkeit sortiert ist. Die Anzahl der Elemente hängt vom Wert ab, den Sie an den alternatives-Hinweis übergeben haben. Sie könnten dieses Array verwenden, um dem Nutzer eine Auswahl an möglichen Übereinstimmungen zu präsentieren und ihn dazu zu bringen, eine Option auszuwählen. 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, das im folgenden Abschnitt erläutert wird.

Detaillierte Statistiken mit Segmentierungsergebnissen

Falls die Zielplattform dies unterstützt, kann das Vorhersageobjekt auch ein Segmentierungsergebnis enthalten. Dies ist ein Array, das das gesamte erkannte Handschriftsegment, eine Kombination aus dem erkannten, nutzererkennbaren Zeichen (grapheme) und seiner Position im erkannten Text (beginIndex, endIndex) sowie den Strichen und Punkten enthält, mit denen es erstellt wurde.

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 werden Felder gezeichnet

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. Durch Klicken auf die Schaltfläche in der unteren rechten Ecke des Bearbeitungssteuerelements aktivieren Sie den Zeichenmodus. Wenn Sie die Zeichnung fertiggestellt haben, beginnt die Webkomponente automatisch die Erkennung und fügt den erkannten Text wieder zum Bearbeitungssteuerelement hinzu. Wenn die Handschrifterkennungs-API nicht unterstützt wird oder die Plattform die angeforderten Funktionen nicht unterstützt, wird die Schaltfläche zum Bearbeiten ausgeblendet. Die grundlegenden Bearbeitungssteuerelemente können jedoch weiterhin als <textarea> verwendet werden.

Die Webkomponente bietet Eigenschaften und Attribute, um das Erkennungsverhalten von außen zu definieren, 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 mithilfe dieser Demo zu Glitch ausprobieren. Sehen Sie sich auch den Quellcode an. 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 unter Zugriff auf leistungsstarke Webplattform-Funktionen steuern definierten Kernprinzipien wie Nutzerkontrolle, Transparenz und Ergonomie entwickelt und implementiert.

Nutzersteuerung

Die Handschrifterkennungs-API kann vom Nutzer nicht deaktiviert werden. Es ist nur für Websites verfügbar, die über HTTPS bereitgestellt werden, und kann nur aus dem Browserkontext auf oberster Ebene aufgerufen werden.

Transparenz

Es wird nicht angezeigt, 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.

Berechtigungstreue

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

Feedback

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

Erzähl uns etwas über das API-Design

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

Problem mit der Implementierung melden

Hast du einen Fehler bei der Implementierung von Chromium 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 zur Reproduktion an. Geben Sie dann Blink>Handwriting in das Feld Komponenten ein. Glitch eignet sich hervorragend, um schnelle und einfache Reproduktionen zu teilen.

Unterstützung für die API anzeigen

Möchten Sie die Handwriting Recognition API verwenden? Dein öffentlicher Support hilft dem Chromium-Team, Funktionen zu priorisieren, und zeigt anderen Browseranbietern, wie wichtig der Support für sie ist.

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

Danksagungen

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