Interfejs Handwriting Recognition API umożliwia rozpoznawanie tekstu z pisma odręcznego w czasie rzeczywistym.
Czym jest interfejs Handwriting Recognition API?
Interfejs Handwriting Recognition API umożliwia przekształcanie pisma odręcznego (atramentu) użytkowników w tekst. Niektóre systemy operacyjne od dawna zawierają takie interfejsy API, a dzięki tej nowej funkcji Twoje aplikacje internetowe mogą wreszcie korzystać z tej funkcjonalności. Konwersja odbywa się 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 implementuje tzw. rozpoznawanie „online” lub w czasie zbliżonym do rzeczywistego. Oznacza to, że odręczne pismo jest rozpoznawane podczas rysowania przez użytkownika poprzez rejestrowanie i analizowanie 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 pociągnięć pióra.
Sugerowane przypadki użycia interfejsu Handwriting Recognition API
Przykłady zastosowań:
- Aplikacje do robienia notatek, w których użytkownicy chcą zapisywać odręczne notatki i tłumaczyć je na tekst.
- Formularze, w których użytkownicy mogą używać rysika lub wpisywać dane palcem ze względu na ograniczenia czasowe.
- Gry, w których trzeba wpisywać litery lub cyfry, np. krzyżówki, wisielec czy sudoku.
Obecny stan,
Interfejs Handwriting Recognition API jest dostępny od wersji Chromium 99.
Jak korzystać z interfejsu Handwriting Recognition API
Wykrywanie cech
Sprawdź, czy przeglądarka obsługuje tę funkcję, sprawdzając, czy w obiekcie navigator istnieje metoda createHandwritingRecognizer()
:
if ('createHandwritingRecognizer' in navigator) {
// 🎉 The Handwriting Recognition API is supported!
}
Podstawowe pojęcia
Interfejs Handwriting Recognition API przekształca pismo odręczne w tekst niezależnie od metody wprowadzania (mysz, dotyk, rysik). Interfejs API ma 4 główne elementy:
- Punkt reprezentuje miejsce, w którym wskaźnik znajdował się w określonym czasie.
- Pociągnięcie składa się z co najmniej 1 punktu. Rejestrowanie pociągnięcia rozpoczyna się, gdy użytkownik przyłoży wskaźnik (czyli kliknie główny przycisk myszy lub dotknie ekranu rysikiem albo palcem), a kończy się, gdy go podniesie.
- Rysunek składa się z co najmniej 1 kreski. Na tym poziomie odbywa się rzeczywiste rozpoznawanie.
- Rozpoznawanie jest skonfigurowane z oczekiwanym językiem wejściowym. Służy do tworzenia instancji rysunku z zastosowaną konfiguracją modułu rozpoznawania.
Koncepcje te są wdrażane jako konkretne interfejsy i słowniki, o których opowiem za chwilę.
Tworzenie modułu rozpoznawania
Aby rozpoznać tekst z pisma odręcznego, musisz uzyskać instancję klasy HandwritingRecognizer
, wywołując metodę navigator.createHandwritingRecognizer()
i przekazując do niej ograniczenia. Ograniczenia określają model rozpoznawania pisma odręcznego, który ma być używany. 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 jest spełniana w przypadku instancji HandwritingRecognizer
, gdy przeglądarka może zrealizować Twoje żądanie. W przeciwnym razie odrzuci obietnicę z błędem i rozpoznawanie pisma odręcznego nie będzie dostępne. Z tego powodu warto najpierw sprawdzić, czy rozpoznawanie obsługuje określone funkcje rozpoznawania.
Sprawdzanie obsługi modułu rozpoznawania
Wywołując navigator.queryHandwritingRecognizer()
, możesz sprawdzić, czy platforma docelowa obsługuje funkcje rozpoznawania pisma odręcznego, których chcesz używać. Ta metoda przyjmuje ten sam obiekt ograniczenia co metoda navigator.createHandwritingRecognizer()
, który zawiera listę żądanych języków. Jeśli zostanie znaleziony zgodny rozpoznawanie, metoda zwraca obietnicę z obiektem wyniku. W przeciwnym razie obietnica zostanie spełniona z wartością null
.
W tym przykładzie deweloper:
- chce wykrywać teksty w języku angielskim.
- w razie potrzeby otrzymywać alternatywne, mniej prawdopodobne prognozy.
- uzyskać dostęp do wyniku segmentacji, czyli rozpoznanych znaków, w tym punktów i kresek, z których się składają;
const result =
await navigator.queryHandwritingRecognizerSupport({
languages: ['en']
});
console.log(result?.textAlternatives); // true if alternatives are supported
console.log(result?.textSegmentation); // true if segmentation is supported
Jeśli przeglądarka obsługuje funkcję wymaganą przez dewelopera, w obiekcie wyniku zostanie ustawiona wartość 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 wysłać nowe zapytanie dotyczące innego zestawu języków.
Rozpoczęcie rysowania
W aplikacji powinno być dostępne pole wprowadzania, w którym użytkownik może wpisywać tekst odręcznie. Ze względu na wydajność zalecamy wdrożenie tej funkcji za pomocą obiektu canvas. Dokładne wdrożenie tej części wykracza poza zakres tego artykułu, ale możesz zapoznać się z wersją demonstracyjną, aby zobaczyć, jak to zrobić.
Aby rozpocząć nowy rysunek, wywołaj metodę startDrawing()
w rozpoznawaniu. Ta metoda przyjmuje obiekt zawierający różne wskazówki, które pozwalają dostroić algorytm rozpoznawania. Wszystkie wskazówki są opcjonalne:
- Rodzaj wpisywanego tekstu: tekst, adresy e-mail, liczby lub pojedynczy znak.
recognitionType
- Rodzaj urządzenia wejściowego: mysz, dotyk lub rysik (
inputType
). - Poprzedni tekst (
textContext
) - Liczba mniej prawdopodobnych alternatywnych prognoz, które mają zostać zwrócone (
alternatives
) - Lista znaków identyfikujących użytkownika („grafemów”), które użytkownik najprawdopodobniej wpisze.
graphemeSet
Interfejs Handwriting Recognition API dobrze współpracuje z Pointer Events, które zapewniają abstrakcyjny interfejs do korzystania z 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 do rozpoznawania pisma odręcznego jest tworzony automatycznie przy pierwszym wystąpieniu zdarzenia pointerdown
w obszarze pisma odręcznego. Ponieważ pole
pointerType
może być puste lub mieć wartość zastrzeżoną, wprowadziliśmy 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', 'stylus'].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
pointerdown
to również odpowiednie miejsce, aby rozpocząć nowy ruch. Aby to zrobić, utwórz nową instancję HandwritingStroke
. Powinieneś też zapisać bieżący czas jako punkt odniesienia dla kolejnych punktów, które do niego dodasz:
function startStroke(event) {
activeStroke = {
stroke: new HandwritingStroke(),
startTime: Date.now(),
};
addPoint(event);
}
Dodaj punkt
Po utworzeniu pociągnięcia od razu dodaj do niego pierwszy punkt. Później dodasz więcej punktów, więc warto zaimplementować logikę tworzenia punktów w osobnej metodzie. W poniższym przykładzie metoda addPoint()
oblicza czas, który upłynął od sygnatury czasowej odniesienia.
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żącego pociągnięcia.
function addPoint(event) {
const timeElapsed = Date.now() - activeStroke.startTime;
activeStroke.stroke.addPoint({
x: event.offsetX,
y: event.offsetY,
t: timeElapsed,
});
}
Procedura obsługi zdarzeń pointermove
jest wywoływana, gdy wskaźnik jest przesuwany po ekranie. Te punkty
muszą zostać dodane również do pociągnięcia. Zdarzenie może też zostać wywołane, jeśli 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 ruch, 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ć pociągnięcie do rysunku, wywołując jego metodę addStroke()
. W tym przykładzie resetowany jest też obiekt activeStroke
, więc procedura obsługi pointermove
nie doda punktów do ukończonego pociągnięcia.
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 tym przykładzie po każdym ukończonym pociągnięciu uruchamiana 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 jest spełniana przez tablicę prognoz uporządkowanych według prawdopodobieństwa. Liczba elementów zależy od wartości przekazanej do wskazówki alternatives
. Możesz użyć tej tablicy, aby przedstawić użytkownikowi wybór możliwych dopasowań i umożliwić mu wybranie opcji. Możesz też po prostu wybrać najbardziej prawdopodobną prognozę, tak jak w tym przykładzie.
Obiekt prognozy zawiera rozpoznany tekst i opcjonalny wynik segmentacji, który omówię w następnej sekcji.
Szczegółowe statystyki z wynikami segmentacji
Jeśli platforma docelowa to obsługuje, obiekt prognozy może też zawierać wynik segmentacji.
Jest to tablica zawierająca wszystkie rozpoznane segmenty pisma odręcznego, czyli kombinację rozpoznanego znaku identyfikowanego przez użytkownika (grapheme
) wraz z jego pozycją w rozpoznanym tekście (beginIndex
, endIndex
) oraz pociągnięciami i punktami, 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 obszarze roboczym.
Pełne rozpoznawanie
Po zakończeniu rozpoznawania możesz zwolnić zasoby, wywołując metodę clear()
na obiekcie HandwritingDrawing
i metodę finish()
na obiekcie HandwritingRecognizer
:
drawing.clear();
recognizer.finish();
Prezentacja
Komponent internetowy <handwriting-textarea>
implementuje stopniowo ulepszany element sterujący edycją, który umożliwia rozpoznawanie pisma odręcznego. Kliknięcie przycisku w prawym dolnym rogu elementu sterującego edycją aktywuje tryb rysowania. Po zakończeniu rysowania komponent internetowy automatycznie rozpocznie rozpoznawanie i doda rozpoznany tekst z powrotem do elementu sterującego edycją. Jeśli interfejs Handwriting Recognition API nie jest w ogóle obsługiwany lub platforma nie obsługuje żądanych funkcji, przycisk edycji zostanie ukryty. Podstawowe narzędzie do edycji pozostaje jednak dostępne jako <textarea>
.
Komponent internetowy udostępnia właściwości i atrybuty, które umożliwiają zdefiniowanie zachowania 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ć informacje o zmianach wartości, możesz nasłuchiwać zdarzenia input
.
Możesz wypróbować komponent, korzystając z tej wersji demonstracyjnej na GitHubie. Sprawdź też kod źródłowy. Aby użyć kontrolki w aplikacji, pobierz ją z npm.
Zabezpieczenia i uprawnienia
Zespół Chromium zaprojektował i wdrożył interfejs Handwriting Recognition API zgodnie z podstawowymi zasadami określonymi w dokumencie Controlling Access to Powerful Web Platform Features, w tym kontrolą użytkownika, przejrzystością i ergonomią.
Kontrola sprawowana przez użytkowników
Użytkownik nie może wyłączyć interfejsu Handwriting Recognition API. Jest dostępny tylko w przypadku stron internetowych dostarczanych przez HTTPS i może być wywoływany tylko z kontekstu przeglądania najwyższego poziomu.
Przejrzystość
Nie ma informacji o tym, czy rozpoznawanie pisma odręcznego jest aktywne. Aby zapobiec odciskowi cyfrowemu, przeglądarka stosuje środki zaradcze, takie jak wyświetlanie użytkownikowi prośby o uprawnienia, gdy wykryje możliwe nadużycie.
Trwałość uprawnień
Interfejs Handwriting Recognition API nie wyświetla obecnie żadnych próśb o uprawnienia. Dlatego nie trzeba w żaden sposób utrwalać uprawnień.
Prześlij opinię
Zespół Chromium chce poznać Twoje wrażenia związane z korzystaniem z interfejsu Handwriting Recognition API.
Opisz projekt interfejsu API
Czy w API jest coś, co nie działa tak, jak oczekujesz? Czy brakuje metod lub właściwości, które są potrzebne do realizacji Twojego pomysłu? Masz pytania lub uwagi dotyczące modelu zabezpieczeń? Zgłoś problem ze specyfikacją w odpowiednim repozytorium GitHub lub dodaj swoje uwagi do istniejącego problemu.
Zgłaszanie problemu z implementacją
Czy udało Ci się znaleźć 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 i wpisz Blink>Handwriting
w polu Komponenty.
Wyrażanie poparcia dla interfejsu API
Czy zamierzasz używać interfejsu Handwriting Recognition API? Twoje publiczne wsparcie pomaga zespołowi Chromium określać priorytety funkcji i pokazuje innym dostawcom przeglądarek, jak ważne jest ich wspieranie.
Opisz, jak zamierzasz z niej korzystać, w wątku na forum WICG Discourse. Wyślij tweeta do @ChromiumDev z hasztagiem #HandwritingRecognition
i napisz, gdzie i jak korzystasz z tej funkcji.
Przydatne linki
- Wyjaśnienie
- Wersja robocza specyfikacji
- Repozytorium GitHub
- ChromeStatus
- Błąd w Chromium
- Sprawdzenie przez TAG
- Zamiar utworzenia prototypu
- Wątek WebKit-Dev
- Stan standardów Mozilli
Podziękowania
Ten dokument został sprawdzony przez Joe Medleya, Honglina Yu i Jieweia Qiana.