Rozpoznawaj pismo odręczne użytkowników

Interfejs Handwriting Recognition API umożliwia rozpoznawanie tekstu z odręcznego wpisu w czasie jego wprowadzania.

Czym jest interfejs Handwriting Recognition API?

Interfejs Handwriting Recognition API umożliwia konwertowanie pisma odręcznego (tuszu) użytkowników na tekst. Niektóre systemy operacyjne od dawna zawierają takie interfejsy API, a dzięki nowej funkcji Twoje aplikacje internetowe mogą w końcu z nich korzystać. Konwersja jest przeprowadzana bezpośrednio na urządzeniu użytkownika, działa nawet w trybie offline, a wszystko to bez dodawania żadnych bibliotek ani usług innych firm.

Ten interfejs API implementuje tak zwane rozpoznawanie „on-line” lub w czasie zbliżonym do rzeczywistego. Oznacza to, że rozpoznawanie pisma odręcznego odbywa się w miarę rysowania przez użytkownika, a nie pojedynczych ruchów. W przeciwieństwie do procedur „offline”, takich jak optyczne rozpoznawanie znaków (OCR), w których znany jest tylko produkt końcowy, algorytmy online mogą zapewniać wyższy poziom dokładności dzięki dodatkowym sygnałom, takim jak sekwencja czasowa i nacisk poszczególnych pociągnięć pióra.

Sugerowane przypadki użycia interfejsu Handwriting Recognition API

Przykładowe zastosowania:

  • Aplikacje do tworzenia notatek, w których użytkownicy chcą skanować odręczne notatki i przekształcać je w tekst.
  • Aplikacje do wypełniania formularzy, w których użytkownicy mogą używać pióra lub palca ze względu na ograniczony czas.
  • gry wymagające wpisywania liter lub cyfr, takie jak krzyżówki, wisielca czy sudoku;

Obecny stan,

Interfejs Handwriting Recognition API jest dostępny od wersji Chromium 99.

Jak korzystać z interfejsu API rozpoznawania pisma odręcznego

Wykrywanie cech

Aby wykryć obsługę w przeglądarce, sprawdź, czy w obiekcie navigator istnieje metoda createHandwritingRecognizer():

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

Podstawowe pojęcia

Interfejs Handwriting Recognition API konwertuje pismo odręczne na tekst niezależnie od metody wprowadzania (mysz, ekran dotykowy, pióro). Interfejs API ma 4 główne elementy:

  1. Punkt wskazuje miejsce, w którym znajdował się wskaźnik w określonym momencie.
  2. Styl składa się z jednego lub większej liczby punktów. Rejestrowanie pojedynczego gestu zaczyna się, gdy użytkownik naciśnie wskaźnik (czyli kliknie główny przycisk myszy lub dotknie ekranu piórem bądź palcem) i kończy się, gdy wskaźnik zostanie odsunięty.
  3. Rysunek składa się z co najmniej 1 pociągnięcia. Na tym poziomie odbywa się rzeczywiste rozpoznawanie.
  4. Rozpoznawanie jest skonfigurowane z oczekiwanym językiem wprowadzania. Pozwala utworzyć instancję rysunku z zastosowaną konfiguracją rozpoznawania.

Pojęcia te są implementowane jako specjalne interfejsy i słowniki, które omówimy za chwilę.

Podstawowe elementy interfejsu ręcznie pisemne rozpoznawanie pisma Rozpoznawanie odbywa się na poziomie rysunku.

Tworzę moduł rozpoznawania

Aby rozpoznawać tekst napisany odręcznie, musisz uzyskać instancję HandwritingRecognizer, wywołując metodę navigator.createHandwritingRecognizer() i przekazując do niej ograniczenia. Ograniczenia określają model rozpoznawania pisma odręcznego, którego należy używać. Obecnie możesz określić listę języków w kolejności preferencji:

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

Metoda zwraca obietnicę, która zostanie spełniona z instancją HandwritingRecognizer, gdy przeglądarka może spełnić Twoje żądanie. W przeciwnym razie usługa odrzuci obietnicę z błędem, a rozpoznawanie pisma ręcznego będzie niedostępne. Dlatego najpierw skontaktuj się z zespołem pomocy usługi OCR, aby uzyskać informacje o konkretnych funkcjach rozpoznawania.

Zapytanie o pomoc dotyczącą rozpoznawania

Wywołując funkcję navigator.queryHandwritingRecognizerSupport(), możesz sprawdzić, czy platforma docelowa obsługuje funkcje rozpoznawania pisma odręcznego, których chcesz użyć. W tym przykładzie deweloper:

  • chce wykrywać teksty w języku angielskim
  • otrzymywać alternatywne, mniej prawdopodobne prognozy, jeśli są dostępne.
  • uzyskać dostęp do wyniku podziału na segmenty, czyli rozpoznanych znaków, w tym punktów i elementów tworzących te znaki;
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

Metoda zwraca obietnicę, która zwraca obiekt wyniku. Jeśli przeglądarka obsługuje funkcję określoną przez dewelopera, jej wartość zostanie ustawiona na true. W przeciwnym razie zostanie ustawiona wartość false. Możesz użyć tych informacji, aby włączyć lub wyłączyć określone funkcje w aplikacji albo zmienić zapytanie i wysłać nowe.

Rozpocznij rysunek

W aplikacji powinna się znajdować obszar wprowadzania danych, w którym użytkownik może ręcznie wpisywać tekst. Ze względu na wydajność zalecamy implementację za pomocą obiektu canvas. W tym artykule nie omawiamy dokładnego wdrożenia tej części, ale jak to zrobić, możesz się dowiedzieć w prezentacji.

Aby rozpocząć nowe rysowanie, wywołaj metodę startDrawing() w rozpoznawaczu. Ta metoda przyjmuje obiekt zawierający różne wskazówki, aby dostosować algorytm rozpoznawania. Wszystkie wskazówki są opcjonalne:

  • Rodzaj wprowadzanego tekstu: tekst, adresy e-mail, cyfry lub pojedynczy znak (recognitionType)
  • Typ urządzenia wejściowego: myszka, dotyk lub rysik (inputType)
  • Poprzedni tekst (textContext)
  • Liczba mniej prawdopodobnych prognoz alternatywnych, które mają zostać zwrócone (alternatives)
  • Lista znaków („grafemów”) rozpoznawalnych przez użytkownika, które prawdopodobnie wpisze (graphemeSet)

Interfejs HandWrite Recognition API dobrze współpracuje ze zdarzeniami wskaźnika, które udostępniają abstrakcyjny interfejs do przetwarzania danych wejściowych z dowolnego urządzenia wskazującego. Argumenty zdarzenia związanego z wskaźnikiem zawierają typ używanego wskaźnika. Oznacza to, że możesz automatycznie określać typ danych wejściowych za pomocą zdarzeń wskaźnika. W tym przykładzie rysunek do rozpoznawania pisma odręcznego jest automatycznie tworzony po pierwszym wystąpieniu zdarzenia pointerdown w obszarze pisma odręcznego. Pole pointerType może być puste lub zawiera zastrzeżoną wartość, dlatego wprowadziłem kontrolę spójności, aby mieć pewność, że dla typu danych wejściowych rysunku ustawione są tylko obsługiwane wartości.

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);
});

Dodawanie obrysu

Zdarzenie pointerdown to też odpowiednie miejsce na rozpoczęcie nowego skoku. Aby to zrobić, utwórz nową instancję instancji HandwritingStroke. Musisz też przechowywać bieżący czas jako punkt odniesienia dla kolejnych punktów dodawanych do niego:

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

Dodaj punkt

Po utworzeniu kreski musisz bezpośrednio dodać do niej pierwszy punkt. Ponieważ później dodasz więcej punktów, warto zastosować logikę tworzenia punktów w osobnej metodzie. W tym przykładzie metoda addPoint() oblicza upłynięty czas od sygnatury czasowej odniesienia. Informacje o czasie są opcjonalne, ale mogą poprawić jakość rozpoznawania. Następnie odczytuje współrzędne X i Y z zdarzenia związanego z wskaźnikiem i doda punkt do bieżącego skrótu.

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

Obsługa zdarzenia pointermove jest wywoływana, gdy wskaźnik jest przenoszony po ekranie. Te punkty muszą też zostać dodane do obrysowania. Zdarzenie to można też wywołać, gdy wskaźnik nie jest w trybie „w dół”, np. podczas przesuwania kursora po ekranie bez naciskania przycisku myszy. Moduł obsługi zdarzeń w tym przykładzie sprawdza, czy istnieje aktywny obrys, i dodaje do niego nowy punkt.

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

Rozpoznawanie tekstu

Gdy użytkownik ponownie podniesie wskaźnik, możesz dodać ślad do rysunku, wywołując metodę addStroke(). W tym przykładzie activeStroke jest też resetowany, więc moduł pointermove nie będzie dodawał punktów do zakończonego skoku.

Następnie należy rozpoznać dane wejściowe użytkownika, wywołując metodę getPrediction() na rysunku. Rozpoznawanie trwa zwykle mniej niż kilkaset milisekund, więc w razie potrzeby możesz wielokrotnie uruchamiać prognozy. W poniższym przykładzie uruchamiana jest nowa prognoza po każdym zakończeniu pociągnięcia.

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));
});

Ta metoda zwraca obietnicę rozwiązaną z tablicą prognoz uporządkowanych według prawdopodobieństwa. Liczba elementów zależy od wartości przekazanej do podpowiedzi alternatives. Możesz użyć tej tablicy, aby przedstawić użytkownikowi wybór możliwych dopasowań i poprosić go o wybór odpowiedniej opcji. Możesz też wybrać najbardziej prawdopodobną prognozę, co zrobię w przykładzie.

Obiekt przewidywania zawiera rozpoznany tekst i opcjonalny wynik podziału na segmenty, o którym opowiem w następnej sekcji.

Szczegółowe statystyki z wynikami podziału na segmenty

Jeśli platforma docelowa obsługuje tę funkcję, obiekt prognozy może też zawierać wynik podziału na segmenty. Jest to tablica zawierająca wszystkie rozpoznane segmenty pisma odręcznego, czyli kombinację rozpoznanego znaku, który może zidentyfikować użytkownik (grapheme), wraz z jego pozycją w rozpoznanym tekście (beginIndex, endIndex) oraz pociągnięciami i punktami, które go tworzą.

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);
      });
    },
  );
}

Możesz użyć tych informacji, aby ponownie zlokalizować rozpoznane grafemy na płótnie.

Wokół każdego rozpoznanego grafemu są rysowane pola

Pełne rozpoznawanie

Po zakończeniu rozpoznawania możesz zwolnić zasoby, wywołując metodę clear() w HandwritingDrawing i finish() w HandwritingRecognizer:

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

Prezentacja

Komponent internetowy <handwriting-textarea> implementuje uzyskiwanie coraz lepszych wyników w edytowaniu za pomocą sterowania umożliwiającego rozpoznawanie pisma odręcznego. Kliknięcie przycisku w prawym dolnym rogu elementu sterującego edycją powoduje włączenie trybu rysowania. Po zakończeniu rysowania komponent internetowy automatycznie rozpocznie rozpoznawanie i doda rozpoznany tekst do elementu sterującego edycją. Jeśli interfejs API rozpoznawania pisma ręcznego nie jest w ogóle obsługiwany lub platforma nie obsługuje żądanych funkcji, przycisk edycji będzie ukryty. Jednak podstawowe narzędzie do edycji nadal będzie dostępne jako <textarea>.

Komponent internetowy udostępnia właściwości i atrybuty, które definiują zachowanie rozpoznawania z zewnątrz, w tym languages i recognitiontype. Zawartość elementu sterującego możesz ustawić za pomocą atrybutu value:

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

Aby otrzymywać powiadomienia o zmianach wartości, możesz nasłuchiwać zdarzenia input.

Możesz wypróbować ten komponent, korzystając z tego demonstracyjnego projektu na Glitch. Zajrzyj też do kodu źródłowego. Aby użyć tej opcji w aplikacji, pobierz ją z npm.

Zabezpieczenia i uprawnienia

Zespół Chromium zaprojektował i wdrożył interfejs Handwriting Recognition API, korzystając z podstawowych zasad określonych w artykule Kontrolowanie dostępu do zaawansowanych funkcji platformy internetowej, w tym kontroli użytkownika, przejrzystości i ergonomiki.

Kontrola użytkownika

Użytkownik nie może wyłączyć interfejsu Handwriting Recognition API. Jest ona dostępna tylko w przypadku witryn dostarczanych przez HTTPS i może być wywoływana tylko z kontekstu przeglądania na najwyższym poziomie.

Przejrzystość

Nie ma informacji o tym, czy rozpoznawanie pisma odręcznego jest aktywne. Aby zapobiec pobieraniu odcisków palców, przeglądarka stosuje środki zaradcze, takie jak wyświetlanie użytkownikowi prośby o udzielenie uprawnień, gdy wykryje możliwe nadużywanie.

Trwałość uprawnień

Interfejs Handwriting Recognition API nie wyświetla obecnie żadnych próśb o przyznanie uprawnień. Dlatego uprawnienia nie muszą być w żaden sposób utrwalane.

Prześlij opinię

Zespół Chromium chce dowiedzieć się więcej o Twoich doświadczeniach z interfejsem ręcznie pisma odręcznego Recognition API.

Prześlij informacje o projektowaniu interfejsu API

Czy coś w interfejsie API nie działa zgodnie z oczekiwaniami? A może brakuje metod lub właściwości, których potrzebujesz do wdrożenia swojego pomysłu? Masz pytanie lub komentarz na temat modelu zabezpieczeń? Zgłoś problem ze specyfikacją w odpowiednim repozytorium GitHub lub podziel się uwagami na temat istniejącego problemu.

Zgłaszanie problemów z implementacją

Czy wystąpił błąd związany z implementacją Chromium? Czy implementacja różni się od specyfikacji? Zgłoś błąd na stronie new.crbug.com. Podaj jak najwięcej szczegółów, proste instrukcje odtwarzania błędu i wpisz Blink>Handwriting w polu Składniki. Usługa Glitch świetnie nadaje się do szybkiego i łatwego udostępniania poprawek.

Pokaż informacje o pomocy dotyczącej interfejsu API

Zamierzasz używać interfejsu Handwriting Recognition API? Twoje publiczne wsparcie pomaga zespołowi Chromium priorytetowo traktować funkcje i pokazuje innym dostawcom przeglądarek, jak ważne jest, aby wspierać te funkcje.

W wątku na forum Discourse dotyczącej WGICh opisz, jak zamierzasz go używać. Wyślij tweeta do @ChromiumDev, używając hashtaga #HandwritingRecognition, i podaj, gdzie i jak go używasz.

Podziękowania

Ten dokument został sprawdzony przez Joe Medley, Honglin Yu i Jiewei Qian.