Rozpoznawaj pismo odręczne użytkowników

Interfejs API rozpoznawania pisma odręcznego umożliwia rozpoznawanie tekstu pisanego odręcznie.

Czym jest interfejs API rozpoznawania pisma odręcznego?

Interfejs API rozpoznawania pisma odręcznego umożliwia konwertowanie pisma odręcznego (atramentu) przez użytkowników na tekst. Niektóre systemy operacyjne od dawna zawierają takie interfejsy API, a dzięki nowym możliwościom Twoje aplikacje internetowe mogą w końcu korzystać z tych funkcji. Konwersja ma miejsce bezpośrednio na urządzeniu użytkownika, działa nawet w trybie offline i nie wymaga dodawania bibliotek ani usług innych firm.

Ten interfejs API obsługuje tzw. rozpoznawanie „on-line” lub rozpoznawanie w czasie zbliżonym do rzeczywistego. Oznacza to, że dane wprowadzone odręcznie są rozpoznawane przez użytkownika podczas rysowania i analizowania pojedynczych pociągnięć. 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ą zapewnić wyższy poziom dokładności dzięki dodatkowym sygnałom, takim jak sekwencja czasowa i nacisk poszczególnych kresek atramentu.

Sugerowane przypadki użycia interfejsu Handwriter Recognition API

Przykłady użycia:

  • Aplikacje do notatek, w których użytkownicy chcą zapisywać odręczne notatki i tłumaczyć je na tekst.
  • Aplikacje w Formularzach, w których ze względu na ograniczenia czasowe użytkownicy mogą korzystać z wprowadzania piórem lub palcem.
  • Gry, które wymagają wpisywania liter lub cyfr, takie jak krzyżówki, wisz czy sudoku.

Obecny stan,

Interfejs API rozpoznawania pisma odręcznego jest dostępny w Chromium 99.

Jak korzystać z interfejsu API rozpoznawania pisma odręcznego

Wykrywanie funkcji

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

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

Podstawowe pojęcia

Interfejs API rozpoznawania pisma odręcznego konwertuje wpisane odręcznie dane na tekst niezależnie od metody (mysz, dotyk, pióro). Interfejs API ma 4 główne elementy:

  1. Punkt wskazuje, gdzie w danym momencie znalazł się wskaźnik.
  2. Styl składa się z co najmniej 1 punktu. Rejestrowanie pociągnięcia rozpoczyna się, gdy użytkownik przesunie wskaźnik w dół (tzn. kliknie główny przycisk myszy albo dotknie ekranu piórem bądź palcem), a kończy, gdy użytkownik podniesie wskaźnik z powrotem w górę.
  3. Rysunek składa się z co najmniej 1 kreski. Na tym poziomie dochodzi do uznania.
  4. Moduł rozpoznawania ma ustawiony oczekiwany język wprowadzania. Służy on do tworzenia instancji rysunków z zastosowaną konfiguracją modułu rozpoznawania.

Te koncepcje są implementowane jako określone interfejsy i słowniki, które wkrótce omówimy.

Podstawowe elementy interfejsu API rozpoznawania pisma odręcznego: co najmniej jeden punkt tworzy kreskę, a jeden lub więcej pociągnięć tworzy rysunek. Rozpoznawanie odbywa się na poziomie rysowania.

Tworzenie modułu rozpoznawania

Aby rozpoznawać tekst z danych wejściowych odręcznych, musisz uzyskać wystąpienie obiektu HandwritingRecognizer, wywołując metodę navigator.createHandwritingRecognizer() i przekazując do niej ograniczenia. Ograniczenia określają, jaki model rozpoznawania pisma odręcznego należy używać. Obecnie możesz określić listę języków w preferowanej kolejności:

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

Metoda zwraca obietnicę rozpoznawaną z wystąpieniem obiektu HandwritingRecognizer, gdy przeglądarka może zrealizować Twoje żądanie. W przeciwnym razie obietnica zostanie odrzucona z błędem, a rozpoznawanie pisma odręcznego nie będzie możliwe. Z tego powodu warto najpierw skontaktować się z obsługą klienta w zakresie określonych funkcji rozpoznawania.

Obsługa modułu rozpoznawania zapytań

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

  • Funkcja chce wykrywać teksty po polsku
  • uzyskiwanie alternatywnych, mniej prawdopodobnych prognoz, gdy są dostępne
  • dostęp do wyniku podziału na segmenty, czyli rozpoznanych znaków, w tym punktów i pociągnięć, które je składają
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ę zamykającą się z obiektem wyniku. Jeśli przeglądarka obsługuje funkcję określoną przez dewelopera, jej wartość zostanie ustawiona na true. W przeciwnym razie ustawiona wartość to false. Możesz użyć tych informacji, aby włączyć lub wyłączyć określone funkcje aplikacji albo dostosować zapytanie i wysłać nowe.

Zacznij rysować

W aplikacji należy umieścić obszar, w którym użytkownik będzie pisać odręcznie. Ze względu na wydajność zalecamy wdrożenie go za pomocą obiektucanvas. W tym artykule nie omawiamy dokładnego wdrożenia tej części, ale możesz obejrzeć prezentację, żeby zobaczyć, jak to zrobić.

Aby utworzyć nowy rysunek, wywołaj metodę startDrawing() w module rozpoznawania. Ta metoda dostosowuje algorytm rozpoznawania do obiektu zawierającego różne wskazówki. Wszystkie wskazówki są opcjonalne:

  • Rodzaj wpisanego tekstu: tekst, adresy e-mail, cyfry lub pojedynczy znak (recognitionType)
  • Typ urządzenia wejściowego: mysz, dotykowe lub pióro (inputType)
  • Poprzedni tekst (textContext)
  • Liczba mniej prawdopodobnych prognoz alternatywnych, które powinny zostać zwrócone (alternatives)
  • Lista znaków umożliwiających identyfikację użytkownika („wykresy”), które prawdopodobnie wpisze użytkownik (graphemeSet)

Interfejs API rozpoznawania pisma odręcznego dobrze współpracuje ze zdarzeniami wskaźnika, które zapewniają abstrakcyjny interfejs do odbierania danych wejściowych z dowolnego urządzenia wskazującego. Argumenty zdarzenia wskaźnika zawierają typ używanego wskaźnika. Oznacza to, że możesz automatycznie określać typ danych wejściowych za pomocą zdarzeń wskaźnika. W poniższym przykładzie rysunek rozpoznawania pisma odręcznego jest tworzony automatycznie przy pierwszym wystąpieniu zdarzenia pointerdown w obszarze pisma odręcznego. Ponieważ właściwość pointerType może być pusta lub ustawiona na zastrzeżoną wartość, wprowadziliśmy kontrolę spójności, aby mieć pewność, że ustawione są tylko obsługiwane wartości dla typu danych wejściowych grafiki.

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

Dodaj styl

Wydarzenie pointerdown to także odpowiednie miejsce na rozpoczęcie nowego stylu. W tym celu utwórz nową instancję obiektu HandwritingStroke. Zapisuj bieżącą godzinę także jako punkt odniesienia dla kolejnych punktów, które zostaną do niej dodane:

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

Dodaj punkt

Po utworzeniu kreski należy dodać do niej pierwszy punkt. Później dodasz kolejne punkty, więc logikę tworzenia punktów warto wdrożyć w osobnej metodzie. W poniższym przykładzie metoda addPoint() oblicza czas, który upłynął, na podstawie sygnatury czasowej odwołania. Informacje czasowe są opcjonalne, ale mogą poprawić jakość rozpoznawania. Następnie odczytuje współrzędne X i Y ze zdarzenia wskaźnika i dodaje punkt do bieżącego pociągnięcia.

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

Moduł obsługi zdarzeń pointermove jest wywoływany, gdy wskaźnik zostanie przesunięty po ekranie. Te punkty również trzeba dodać do linii. Zdarzenie może być też wywoływane, jeśli wskaźnik nie jest w stanie „w dół”, np. gdy przesuwasz kursor po ekranie bez naciśnięcia przycisku myszy. Moduł obsługi zdarzeń z poniższego przykładu sprawdza, czy istnieje aktywny kresek, i dodaje do niej nowy punkt.

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

Rozpoznawaj tekst

Gdy użytkownik ponownie uniesie wskaźnik, możesz dodać kreskę do rysowania, wywołując metodę addStroke(). Poniższy przykład resetuje też activeStroke, więc moduł obsługi pointermove nie doda punktów do ukończonego pociągnięcia.

Następnie należy rozpoznać dane wejściowe użytkownika przez wywołanie metody getPrediction() w rysunku. Rozpoznawanie trwa zwykle mniej niż kilkaset milisekund, więc w razie potrzeby możesz uruchamiać je wielokrotnie. W przykładzie poniżej uruchamiamy nową prognozę po każdym zakończonym kresce.

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 znika za pomocą tablicy prognoz uporządkowanych według prawdopodobieństwa. Liczba elementów zależy od wartości przekazanej wskazówki alternatives. Możesz użyć tej tablicy, aby przedstawić użytkownikowi wybór propozycji i poprosić go o wybranie opcji. Możesz też skorzystać z prognozy najbardziej prawdopodobnej – tak jak to zrobiłem w przykładzie.

Obiekt prognozy zawiera rozpoznany tekst i opcjonalny wynik podziału na segmenty, które omówię w następnej sekcji.

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

Jeśli platforma docelowa jest obsługiwana przez platformę docelową, obiekt prognozy może też zawierać wynik podziału na segmenty. Jest to tablica zawierająca wszystkie rozpoznawane segmenty pisma odręcznego, czyli połączenie rozpoznanego znaku umożliwiającego identyfikację użytkownika (grapheme) i jego pozycji w rozpoznawanym tekście (beginIndex, endIndex) oraz kresek i punktów, które go powodują.

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 odnaleźć rozpoznawane grafemy na obszarze roboczym.

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

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

Demonstracyjny

Komponent internetowy <handwriting-textarea> implementuje stopniowo ulepszoną kontrolkę edycji umożliwiającą rozpoznawanie pisma odręcznego. Klikając przycisk w prawym dolnym rogu elementu sterującego edycji, aktywujesz tryb rysowania. Po zakończeniu rysowania komponent internetowy automatycznie uruchomi rozpoznawanie i doda rozpoznany tekst z powrotem do elementu sterującego do edycji. Jeśli interfejs API rozpoznawania pisma odręcznego w ogóle nie jest obsługiwany lub platforma nie obsługuje żądanych funkcji, przycisk edycji będzie ukryty. Nadal możesz jednak korzystać z podstawowych opcji edycji jako <textarea>.

Komponent internetowy udostępnia właściwości i atrybuty, które definiują działanie rozpoznawania z zewnątrz, m.in. 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ć informacje o zmianach wartości, możesz nasłuchiwać zdarzenia input.

Możesz wypróbować komponent, korzystając z tej wersji demonstracyjnej usługi Glitch. Zapoznaj się też z kodem źródłowym. Aby użyć elementu sterującego w aplikacji, uzyskaj go z npm.

Zabezpieczenia i uprawnienia

Zespół Chromium opracował i wdrożył interfejs API rozpoznawania pisma odręcznego, opierając się na podstawowych zasadach opisanych w artykule Kontrolowanie dostępu do zaawansowanych funkcji platformy internetowej, takich jak kontrola użytkownika, przejrzystość i ergonomia.

Kontrola użytkowników

Użytkownik nie może wyłączyć interfejsu Handwriter Recognition API. Jest dostępna tylko w przypadku witryn dostarczanych przez protokół HTTPS i może być wywoływana wyłącznie w kontekście przeglądania najwyższego poziomu.

Przejrzystość

Brak oznaczenia, czy rozpoznawanie pisma odręcznego jest aktywne. Aby zapobiec korzystaniu z odcisków cyfrowych, przeglądarka stosuje środki zaradcze, takie jak wyświetlanie użytkownikowi prośby o przyznanie uprawnień w przypadku wykrycia możliwego nadużycia.

Trwałość uprawnień

Interfejs API rozpoznawania pisma odręcznego nie wyświetla obecnie żadnych próśb o przyznanie uprawnień. W związku z tym uprawnienie nie musi być w żaden sposób trwałe.

Prześlij opinię

Zespół Chromium chce poznać Twoją opinię na temat interfejsu API rozpoznawania pisma odręcznego.

Opowiedz nam o konstrukcji interfejsu API

Czy jest coś, co nie działa w interfejsie API zgodnie z oczekiwaniami? A może brakuje metod lub właściwości, które potrzebujesz do realizacji pomysłu? Masz pytanie lub komentarz na temat modelu zabezpieczeń? Zgłoś problem ze specyfikacją w odpowiednim repozytorium GitHub lub dodaj swoje uwagi na temat istniejącego problemu.

Zgłoś problem z implementacją

Czy w trakcie implementacji Chromium wystąpił błąd? 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 i wpisz Blink>Handwriting w polu Komponenty. Glitch to doskonały sposób na szybkie i łatwe udostępnianie kopii.

Wyraź wsparcie dla interfejsu API

Czy zamierzasz korzystać z interfejsu API rozpoznawania pisma odręcznego? Twoja publiczna pomoc pomaga zespołowi Chromium priorytetowo traktować funkcje i pokazuje innym dostawcom, jak ważne jest ich wsparcie.

W wątku dyskusyjnym WICG opisz, jak zamierzasz korzystać z tej funkcji. Wyślij tweeta na adres @ChromiumDev, używając hashtagu #HandwritingRecognition, i daj nam znać, gdzie i jak go używasz.

Podziękowania

Ten artykuł został opublikowany przez Joe Medley, Honglin Yu i Jiewei Qian. Baner powitalny autorstwa: Samir Bouaked w serwisie Unsplash.