Rozpoznawaj pismo odręczne użytkowników

Interfejs ręcznie rozpoznawanego pisma odręcznego umożliwia rozpoznawanie tekstu pochodzącego z wprowadzanych odręcznie tekstów.

Czym jest interfejs Handwriter Recognition API?

Interfejs API rozpoznawania pisma odręcznego umożliwia konwertowanie pisma odręcznego (atramentem) użytkowników na tekst. Niektóre systemy operacyjne od dawna zawierają takie interfejsy API, a dzięki nowej funkcji aplikacje internetowe mogą w końcu z niej 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.

Implementuje tak zwane rozpoznawanie „online” lub rozpoznawanie w czasie zbliżonym do rzeczywistego. Oznacza to, że odręczne znaki wpisywane przez użytkownika są rozpoznawane podczas rysowania i analizowania poszczególnych pociągnięć. W przeciwieństwie do procedur „offline”, takich jak optyczne rozpoznawanie znaków (OCR), gdzie 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 ciśnienie poszczególnych naciśnięć atramentu.

Sugerowane przypadki użycia interfejsu Handwrite Recognition API

Przykładowe zastosowania:

  • Aplikacje do robienia notatek, w których użytkownicy chcą zapisywać odręczne notatki i tłumaczyć je na tekst.
  • Formularze, w których ze względu na ograniczenia czasowe użytkownicy mogą korzystać z pisania palcem lub piórem.
  • Gry wymagające wpisania liter lub cyfr, np. krzyżówki, hangman czy sudoku.

Obecny stan,

Interfejs ręcznie rozpoznawanego pisma odręcznego jest dostępny w Chromium 99.

Jak korzystać z interfejsu Handwriter Recognition API

Wykrywanie cech

Wykryj obsługę przeglądarki, sprawdzając, czy w obiekcie navigator istnieje metoda createHandwritingRecognizer():

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

Podstawowe pojęcia

Interfejs API rozpoznawania pisma odręcznego konwertuje napisane odręcznie dane wejściowe na tekst niezależnie od metody wprowadzania (mysz, dotyku, długopisu). Interfejs API składa się z 4 głównych elementów:

  1. Punkt wskazuje miejsce, w którym w danym momencie znajdował się wskaźnik.
  2. Styl składa się z jednego lub większej liczby punktów. Rejestrowanie pociągnięcia rozpoczyna się, gdy użytkownik najedzie kursorem na dół (np. kliknie główny przycisk myszy albo dotknie ekranu rysikiem lub palcem), a zakończy się, gdy ponownie podniesie wskaźnik w górę.
  3. Rysunek składa się z jednego lub kilku pociągnięć. 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 HandWrite Recognition API: co najmniej 1 punkt tworzy kreskę – jeden lub więcej pociągnięć to rysunek, który jest tworzony przez moduł rozpoznawania. Rzeczywiste rozpoznawanie odbywa się na poziomie rysunku.

Tworzę moduł rozpoznawania

Aby rozpoznać 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 podać listę języków według preferencji:

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

Ta metoda zwraca obietnicę rozwiązaną za pomocą instancji HandwritingRecognizer, gdy przeglądarka może zrealizować Twoje żądanie. W przeciwnym razie odrzuci obietnicę z błędem, a rozpoznawanie pisma odręcznego nie będzie dostępne. Dlatego warto najpierw wysłać zapytanie o obsługę określonych funkcji rozpoznawania.

Obsługa modułu rozpoznawania zapytań

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

  • Chce wykrywać teksty po angielsku
  • otrzymuj alternatywne, mniej prawdopodobne prognozy, gdy są dostępne
  • uzyskać dostęp do wyniku segmentacji, tj. rozpoznanych znaków, w tym punktów i kresek, które z nich składają się
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ę rozwiązaną za pomocą obiektu wynikowego. Jeśli przeglądarka obsługuje funkcję określoną przez dewelopera, jej wartość zostanie ustawiona na true. W przeciwnym razie ma ona wartość false. Na podstawie tych informacji możesz włączać i wyłączać określone funkcje aplikacji albo dostosować zapytanie i wysłać nowe.

Rozpocznij rysunek

W aplikacji musisz udostępnić obszar do wprowadzania danych, w którym użytkownik wpisuje odręczne teksty. Ze względu na wydajność zalecamy wdrożenie tego rozwiązania 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ąć nowy rysunek, wywołaj metodę startDrawing() w module rozpoznawania. Ta metoda wykorzystuje obiekt zawierający różne wskazówki, aby dostroić 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 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 („grafemów”), które użytkownik najprawdopodobniej 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 wskaźnika zawierają typ używanego wskaźnika. Oznacza to, że możesz używać zdarzeń wskaźnika do automatycznego określania typu danych wejściowych. W poniższym przykładzie rysunek dotyczący rozpoznawania pisma odręcznego jest tworzony automatycznie przy pierwszym wystąpieniu zdarzenia pointerdown w obszarze pisma odręcznego. Pole pointerType może być puste lub zawiera zastrzeżoną wartość, dlatego wprowadziliśmy kontrolę spójności, aby mieć pewność, że dla typu danych wejściowych 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);
});

Dodaj kreskę

Zdarzenie pointerdown to też dobry moment, by zacząć nowy styl. Aby to zrobić, utwórz nową instancję HandwritingStroke. Zapisz też bieżącą godzinę jako punkt odniesienia przy kolejnych dodawanych do niej punktach:

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 wdrożyć logikę tworzenia punktów w osobnej metodzie. W poniższym przykładzie metoda addPoint() oblicza czas, który upłynął na podstawie sygnatury czasowej odwołania. Informacje o czasie 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żącej linii.

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 przesuwa się po ekranie. Trzeba też dodać punkty do kreski. Zdarzenie to można też wywołać, gdy wskaźnik nie jest w stanie „w dół”, np. podczas przesuwania kursora po ekranie bez naciskania przycisku myszy. Moduł obsługi zdarzeń z poniższego przykładu sprawdza, czy istnieje aktywny styl, i dodaje do niego nowy punkt.

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

Rozpoznaj tekst

Gdy użytkownik ponownie podniesie wskaźnik, możesz dodać do rysunku kreskę, wywołując jego metodę addStroke(). Poniższy przykład resetuje również obiekt activeStroke, więc moduł obsługi pointermove nie będzie dodawać punktów do ukończonego pociągnięcia.

Następnie trzeba rozpoznać dane wejściowe użytkownika przez wywołanie metody getPrediction() na rysunku. Rozpoznawanie trwa zwykle mniej niż kilkaset milisekund, dzięki czemu możesz w razie potrzeby wielokrotnie generować 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 prognozy zawiera rozpoznany tekst i opcjonalny wynik segmentacji, które omówimy w następnej sekcji.

Szczegółowe statystyki z wynikami segmentacji

Jeśli platforma docelowa go obsługuje, obiekt prognozy może też zawierać wynik podziału na segmenty. Jest to tablica zawierająca cały rozpoznany segment pisma odręcznego, kombinację znaku rozpoznawanego przez użytkownika (grapheme) z jego pozycją w rozpoznanym tekście (beginIndex, endIndex) oraz kreski i punktów, które go utworzyły.

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źć rozpoznane grafemy na płótnie.

Każdy rozpoznawany grafem jest rysowany w ramkach

Pełne uznanie

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

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

Pokaz

Komponent internetowy <handwriting-textarea> zawiera stopniowo ulepszone elementy sterujące edycją rozpoznające pismo odręczne. Klikając przycisk w prawym dolnym rogu elementu sterującego edycją, aktywujesz tryb rysowania. Gdy skończysz rysowanie, komponent internetowy automatycznie rozpocznie rozpoznawanie i doda rozpoznany tekst z powrotem do elementu sterującego edycji. Jeśli interfejs API rozpoznawania pisma odręcznego nie jest obsługiwany lub platforma nie obsługuje żądanych funkcji, przycisk edycji będzie ukryty. Podstawowe elementy sterujące edycji można nadal używać jako elementu <textarea>.

Komponent internetowy zawiera właściwości i atrybuty definiujące zachowanie rozpoznawania, 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ć informacje o zmianach wartości, możesz nasłuchiwać zdarzenia input.

Możesz wypróbować komponent, korzystając z tej wersji demonstracyjnej w Glitch. Zajrzyj też do kodu źródłowego. Aby użyć tej opcji w aplikacji, uzyskaj ją z npm.

Bezpieczeństwo i uprawnienia

Zespół Chromium zaprojektował i wdrożył interfejs ręcznie pisma odręcznego zgodnie z podstawowymi zasadami opisanymi w artykule Kontrola nad dostępem do zaawansowanych funkcji platformy internetowej, takimi jak kontrola użytkowników, przejrzystość i ergonomia.

Kontrola użytkowników

Użytkownik nie może wyłączyć interfejsu Handwriter Recognition API. Ta funkcja jest dostępna tylko w przypadku witryn dostarczanych przez HTTPS i może być wywoływana wyłącznie z poziomu przeglądania na najwyższym poziomie.

Przejrzystość

Nie wiadomo, czy rozpoznawanie pisma odręcznego jest aktywne. Aby temu zapobiec, przeglądarka stosuje środki zaradcze, takie jak wyświetlanie użytkownikowi prośby o przyznanie uprawnień w przypadku wykrycia potencjalnego nadużycia.

Trwałość uprawnień

Interfejs HandWrite Recognition API nie wyświetla obecnie żadnych promptów o uprawnieniach. Oznacza to, że uprawnienia nie muszą być w żaden sposób zachowywane.

Prześlij opinię

Zespół Chromium chce dowiedzieć się więcej o Twoich doświadczeniach z interfejsem Handwriter Recognition API.

Opowiedz nam o konstrukcji interfejsu API

Czy jest coś, co nie działa w interfejsie API zgodnie z oczekiwaniami? A może brakuje Ci metod lub właściwości, których potrzebujesz, by zrealizować swój pomysł? 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łoś problem 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 i wpisz Blink>Handwriting w polu Komponenty. Usługa Glitch świetnie nadaje się do szybkiego i łatwego udostępniania poprawek.

Pokaż wsparcie dla interfejsu API

Czy zamierzasz korzystać z interfejsu Handwriter 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.

Poinformuj nas, jak zamierzasz korzystać z tego narzędzia w wątku poświęconym dysku WICG. 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ł napisali Joe Medley, Honglin Yu i Jiewei Qian. Baner powitalny: Samir Bouaked na kanale Unsplash.