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 zachodzi bezpośrednio na urządzeniu użytkownika i działa nawet w trybie offline, bez konieczności 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 odróżnieniu od 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 ograniczenia czasowe.
  • Gry, które wymagają 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 Handwriting Recognition API

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. Ślad linii kreślony za pomocą narzędzia Pędzel składa się z co najmniej 1 punktu. Rejestrowanie pojedynczego pociągnięcia 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ę faktyczne rozpoznawanie.
  4. Rozpoznawca jest skonfigurowany z uwzględnieniem oczekiwanego języka wprowadzania. Służy do tworzenia instancji rysunku z zastosowanej konfiguracji modułu rozpoznawania.

Te koncepcje są implementowane jako konkretne interfejsy i słowniki, o których opowiem za chwilę.

Podstawowe elementy interfejsu Handwriting Recognition API: co najmniej 1 punkt tworzy ślad, a co najmniej 1 ślad tworzy rysunek, który tworzy rozpoznawacz. Rozpoznawanie odbywa się na poziomie rysunku.

Tworzenie modułu rozpoznawania

Aby rozpoznać tekst z odręcznego wpisu, musisz uzyskać instancję funkcji HandwritingRecognizer, wywołując funkcję navigator.createHandwritingRecognizer() i przekazując jej ograniczenia. Ograniczenia określają model rozpoznawania pisma odręcznego, którego należy użyć. 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 zostanie odrzucona 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 do zespołu pomocy rozpoznawacza

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 rysowanie

W aplikacji należy udostępnić 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. Szczegóły implementacji tej części wykraczają poza zakres tego artykułu, ale możesz zapoznać się z prezentacją, aby dowiedzieć się, jak to zrobić.

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 podpowiedzi są opcjonalne:

  • Rodzaj wpisywanego tekstu: tekst, adresy e-mail, cyfry lub pojedynczy znak (recognitionType).
  • Typ urządzenia wejściowego: mysz, dotyk lub pióro (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 Handwriting Recognition API dobrze współpracuje z zdarzeniami wskaźnika, które zapewniają abstrakcyjny interfejs do obsługi 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. Ponieważ parametr pointerType może być pusty lub ustawiony na wartość zastrzeżoną, wprowadziłem sprawdzanie spójności, aby mieć pewność, że dla typu danych rysunku są ustawione 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ę funkcji 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 obrysu dodaj do niego bezpośrednio 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 czasowe 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ą zostać dodane do obrysowania. Zdarzenie może też zostać wywołane, jeśli wskaźnik nie jest w stanie „w dół”, np. gdy przesuwasz kursor 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.

Teraz czas na rozpoznanie danych wejściowych użytkownika przez wywołanie metody getPrediction() na rysunku. Rozpoznawanie trwa zwykle mniej niż kilkaset milisekund, więc w razie potrzeby możesz wielokrotnie uruchamiać prognozy. W tym przykładzie po każdym wykonanym słowie generowana jest nowa prognoza.

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ę, która zwraca tablicę prognoz uporządkowaną 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 możliwe dopasowania i poprosić go o wybranie opcji. Możesz też po prostu wybrać najbardziej prawdopodobne przewidywanie, co robię w tym 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 metodę finish() w HandwritingRecognizer:

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

Prezentacja

Komponent internetowy <handwriting-textarea> implementuje uzyskiwanie coraz lepszych wyników w edytowaniu za pomocą sterowania, które umożliwia rozpoznawanie pisma odręcznego. Kliknięcie przycisku w prawym dolnym rogu elementu sterującego edycją powoduje włączenie trybu rysowania. Gdy skończysz rysować, 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ędzia do edycji pozostają 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. Treść 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 słuchać zdarzenia input.

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

Zabezpieczenia i uprawnienia

Zespół Chromium zaprojektował i wdrżał 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 poznać Twoje wrażenia związane z korzystaniem z interfejsu Handwriting Recognition API.

Poinformuj nas 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 pytania lub uwagi dotyczące modelu bezpieczeństwa? Zgłoś problem ze specyfikacją w odpowiednim repozytorium GitHub lub dodaj swoje uwagi do istniejącego problemu.

Zgłaszanie problemów z implementacją

Czy znalazłeś błąd w implementacji Chromium? A może 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. Glitch to świetne narzędzie do szybkiego i łatwego udostępniania informacji o powtarzających się problemach.

Pokaż informacje o pomocy dotyczącej interfejsu API

Zamierzasz używać interfejsu Handwriting Recognition API? Twoja publiczna pomoc pomaga zespołowi Chromium ustalać priorytety funkcji i pokazuje innym dostawcom przeglądarek, jak ważne jest ich wsparcie.

W wątku na forum Discourse WICG 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.