Riconoscimento della scrittura a mano libera degli utenti

L'API di riconoscimento della scrittura a mano libera ti consente di riconoscere il testo inserito a mano libera in tempo reale.

Che cos'è l'API di riconoscimento della scrittura a mano libera?

L'API di riconoscimento della scrittura a mano libera ti consente di convertire la scrittura a mano libera (ink) degli utenti in testo. Alcuni sistemi operativi includono da molto tempo queste API e, con questa nuova funzionalità, le app web possono finalmente utilizzare questa funzionalità. La conversione avviene direttamente sul dispositivo dell'utente, funziona anche in modalità offline, il tutto senza aggiungere librerie o servizi di terze parti.

Questa API implementa il cosiddetto riconoscimento "online" o quasi in tempo reale. Ciò significa che l'input scritto a mano viene riconosciuto mentre l'utente lo sta disegnando acquisendo e analizzando i singoli tratti. A differenza delle procedure "off-line" come il riconoscimento ottico dei caratteri (OCR), dove è noto solo il prodotto finale, gli algoritmi online possono fornire un livello più elevato di precisione grazie a segnali aggiuntivi come la sequenza temporale e la pressione dei singoli tratti di inchiostro.

Casi d'uso suggeriti per l'API di riconoscimento della scrittura a mano libera

Esempi di utilizzo includono:

  • Applicazioni per prendere appunti in cui gli utenti vogliono acquisire note scritte a mano libera e tradurle in testo.
  • Moduli le applicazioni in cui gli utenti possono utilizzare la penna o l'immissione con le dita a causa di limiti di tempo.
  • Giochi che richiedono l'inserimento di lettere o numeri, come cruciverba, impiccato o sudoku.

Stato attuale

L'API di riconoscimento della scrittura a mano libera è disponibile su (Chromium 99).

Come utilizzare l'API di riconoscimento della scrittura a mano libera

Rilevamento delle caratteristiche

Rileva il supporto del browser controllando l'esistenza del metodo createHandwritingRecognizer() nell'oggetto navigator:

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

Concetti principali

L'API di riconoscimento della scrittura a mano libera converte l'input scritto a mano libera in testo, indipendentemente dal metodo di inserimento (mouse, tocco, penna). L'API ha quattro entità principali:

  1. Un punto rappresenta il punto in cui si trovava il puntatore in un determinato momento.
  2. Un tratto è costituito da uno o più punti. La registrazione di un tratto inizia quando l'utente posiziona il puntatore verso il basso (ovvero fa clic sul pulsante principale del mouse o tocca lo schermo con la penna o il dito) e termina quando solleva di nuovo il puntatore.
  3. Un disegno è costituito da uno o più tratti. Il riconoscimento effettivo avviene a questo livello.
  4. Il riconoscimento viene configurato con la lingua di input prevista. Viene usato per creare l'istanza di un disegno con la configurazione del riconoscimento applicata.

Questi concetti vengono implementati come interfacce e dizionari specifici, di cui parleremo a breve.

Le entità principali dell'API di riconoscimento della scrittura a mano libera: uno o più punti compongono un tratto, uno o più tratti compongono un disegno, creato dal riconoscimento. Il riconoscimento effettivo avviene a livello di disegno.

Creazione di un riconoscimento in corso...

Per riconoscere il testo da un input scritto a mano, devi ottenere un'istanza di un HandwritingRecognizer chiamando navigator.createHandwritingRecognizer() e passandovi vincoli. I vincoli determinano il modello di riconoscimento della scrittura a mano libera da utilizzare. Al momento, puoi specificare un elenco di lingue in ordine di preferenza:

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

Il metodo restituisce una promessa che viene risolta con un'istanza di HandwritingRecognizer quando il browser è in grado di soddisfare la tua richiesta. In caso contrario, la promessa viene rifiutata con un errore e il riconoscimento della scrittura non è disponibile. Per questo motivo, ti consigliamo di chiedere prima all'assistenza del riconoscimento determinate funzioni di riconoscimento.

Esecuzione di query sul supporto del riconoscimento

Chiamando navigator.queryHandwritingRecognizerSupport(), puoi verificare se la piattaforma di destinazione supporta le funzionalità di riconoscimento della scrittura che intendi utilizzare. Nell'esempio seguente, lo sviluppatore:

  • vuole rilevare i testi in inglese
  • ottenere previsioni alternative, meno probabili, se disponibili
  • accedere al risultato della segmentazione, ovvero ai caratteri riconosciuti, inclusi i punti e i tratti che li compongono
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

Il metodo restituisce una promessa che viene risolta con un oggetto risultato. Se il browser supporta la funzionalità specificata dallo sviluppatore, il suo valore verrà impostato su true. In caso contrario, verrà impostato su false. Puoi utilizzare queste informazioni per attivare o disattivare determinate funzioni all'interno dell'applicazione o per modificare la query e inviarne una nuova.

Inizia a disegnare

All'interno dell'applicazione, dovresti offrire un'area di inserimento in cui l'utente effettua le proprie voci scritte a mano. Per migliorare le prestazioni, si consiglia di implementarlo con l'aiuto di un oggetto canvas. L'implementazione esatta di questa parte non rientra nell'ambito di questo articolo, ma puoi fare riferimento alla demo per scoprire come farlo.

Per iniziare un nuovo disegno, chiama il metodo startDrawing() sul riconoscimento. Questo metodo utilizza un oggetto contenente diversi suggerimenti per ottimizzare l'algoritmo di riconoscimento. Tutti i suggerimenti sono facoltativi:

  • Tipo di testo inserito: testo, indirizzi email, numeri o un carattere singolo (recognitionType)
  • Il tipo di dispositivo di input: mouse, tocco o input della penna (inputType)
  • Il testo precedente (textContext)
  • Il numero di previsioni alternative meno probabili che devono essere restituite (alternatives)
  • Un elenco di caratteri identificabili dall'utente ("grafemi") che l'utente molto probabilmente inserirà (graphemeSet)

L'API di riconoscimento della scrittura a mano libera funziona bene con gli eventi puntatore, che forniscono un'interfaccia astratta per consumare l'input da qualsiasi dispositivo di puntamento. Gli argomenti dell'evento puntatore contengono il tipo di puntatore utilizzato. Ciò significa che puoi utilizzare gli eventi puntatore per determinare automaticamente il tipo di input. Nell'esempio seguente, il disegno per il riconoscimento della scrittura a mano libera viene creato automaticamente alla prima occorrenza di un evento pointerdown nell'area di scrittura a mano libera. Poiché pointerType potrebbe essere vuoto o impostato su un valore proprietario, ho introdotto un controllo della coerenza per assicurarmi che siano impostati solo i valori supportati per il tipo di input del disegno.

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

Aggiungi un tratto

L'evento pointerdown è anche il posto giusto per iniziare un nuovo tratto. Per farlo, crea una nuova istanza di HandwritingStroke. Inoltre, devi archiviare l'ora corrente come punto di riferimento per i punti successivi aggiunti:

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

Aggiungi un punto

Dopo aver creato il tratto, devi aggiungere direttamente il primo punto. Man mano che aggiungerai altri punti in seguito, ha senso implementare la logica di creazione dei punti in un metodo separato. Nell'esempio seguente, il metodo addPoint() calcola il tempo trascorso dal timestamp di riferimento. Le informazioni sulla tempistica sono facoltative, ma possono migliorare la qualità del riconoscimento. Quindi, legge le coordinate X e Y dall'evento del puntatore e aggiunge il punto al tratto corrente.

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

Il gestore di eventi pointermove viene chiamato quando il puntatore viene spostato sullo schermo. Anche questi punti devono essere aggiunti al tratto. L'evento può essere generato anche se il puntatore non è in stato "Giù", ad esempio quando sposti il cursore sullo schermo senza premere il pulsante del mouse. Il gestore di eventi dell'esempio seguente controlla se esiste un tratto attivo e vi aggiunge il nuovo punto.

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

Riconoscimento del testo

Quando l'utente solleva di nuovo il puntatore, puoi aggiungere il tratto al disegno chiamando il relativo metodo addStroke(). Anche l'esempio seguente reimposta il activeStroke, quindi il gestore pointermove non aggiungerà punti al tratto completato.

A questo punto, occorre riconoscere l'input dell'utente chiamando il metodo getPrediction() sul disegno. Il riconoscimento richiede in genere meno di qualche centinaio di millisecondi, quindi è possibile eseguire ripetutamente previsioni se necessario. L'esempio seguente esegue una nuova previsione dopo ogni tratto completato.

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

Questo metodo restituisce una promessa che si risolve con un array di previsioni ordinate in base alla probabilità. Il numero di elementi dipende dal valore passato al suggerimento alternatives. Potresti utilizzare questo array per presentare all'utente una scelta di possibili corrispondenze e chiedergli di selezionare un'opzione. In alternativa, puoi basarti sulla previsione più probabile, che è quello che faccio nell'esempio.

L'oggetto Prediction contiene il testo riconosciuto e un risultato di segmentazione facoltativo, che tratterò nella sezione seguente.

Statistiche dettagliate con risultati di segmentazione

Se supportato dalla piattaforma di destinazione, l'oggetto di previsione può contenere anche un risultato di segmentazione. Si tratta di un array contenente tutti i segmenti di scrittura a mano libera riconosciuti, una combinazione del carattere di identificazione dell'utente riconosciuto (grapheme) insieme alla sua posizione nel testo riconosciuto (beginIndex, endIndex), nonché dei tratti e dei punti che lo hanno creato.

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

Puoi utilizzare queste informazioni per individuare di nuovo i grafemi riconosciuti sulla tela.

Intorno a ciascun grafema riconosciuto sono tracciate delle caselle

Riconoscimento completo

Una volta completato il riconoscimento, puoi liberare risorse chiamando il metodo clear() su HandwritingDrawing e il metodo finish() su HandwritingRecognizer:

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

Demo

Il componente web <handwriting-textarea> implementa un controllo di modifica progressivamente migliorato in grado di riconoscere la scrittura a mano libera. Facendo clic sul pulsante nell'angolo in basso a destra del controllo di modifica, attivi la modalità disegno. Una volta completato il disegno, il componente web avvierà automaticamente il riconoscimento e aggiungerà di nuovo il testo riconosciuto al controllo di modifica. Se l'API di riconoscimento della scrittura a mano libera non è supportata o se la piattaforma non supporta le funzionalità richieste, il pulsante di modifica verrà nascosto. Tuttavia, il controllo di modifica di base rimane utilizzabile come <textarea>.

Il componente web offre proprietà e attributi per definire il comportamento di riconoscimento dall'esterno, tra cui languages e recognitiontype. Puoi impostare i contenuti del controllo tramite l'attributo value:

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

Per ricevere informazioni su eventuali modifiche al valore, puoi ascoltare l'evento input.

Puoi provare il componente utilizzando questa demo su Glitch. Assicurati inoltre di dare un'occhiata al codice sorgente. Per utilizzare il controllo nella tua applicazione, ottienilo da npm.

Sicurezza e autorizzazioni

Il team di Chromium ha progettato e implementato l'API di riconoscimento della scrittura a mano libera utilizzando i principi fondamentali definiti in Controllo dell'accesso a funzionalità avanzate della piattaforma web, tra cui controllo degli utenti, trasparenza ed ergonomia.

Controllo utenti

L'API di riconoscimento della scrittura a mano libera non può essere disattivata dall'utente. È disponibile solo per i siti web pubblicati tramite HTTPS e può essere chiamato solo dal contesto di navigazione di primo livello.

Trasparenza

Non viene visualizzata alcuna indicazione relativa al fatto che il riconoscimento della scrittura a mano libera è attivo. Per impedire il fingerprinting, il browser implementa contromisure, ad esempio la visualizzazione di una richiesta di autorizzazione all'utente quando rileva un possibile abuso.

Persistenza dell'autorizzazione

L'API di riconoscimento della scrittura a mano libera attualmente non mostra richieste di autorizzazione. Pertanto, l'autorizzazione non deve essere in alcun modo persistente.

Feedback

Il team di Chromium vuole saperne di più sulla tua esperienza relativa all'API di riconoscimento della scrittura a mano libera.

Parlaci della progettazione dell'API

C'è qualcosa nell'API che non funziona come previsto? Oppure mancano metodi o proprietà per implementare la tua idea? Hai una domanda o un commento sul modello di sicurezza? Invia un problema relativo alle specifiche sul repository GitHub corrispondente o aggiungi le tue opinioni a un problema esistente.

Segnalare un problema con l'implementazione

Hai trovato un bug nell'implementazione di Chromium? Oppure l'implementazione è diversa dalle specifiche? Segnala un bug all'indirizzo new.crbug.com. Assicurati di includere il maggior numero di dettagli possibile, semplici istruzioni per la riproduzione e inserisci Blink>Handwriting nella casella Componenti. Glitch è la soluzione perfetta per condividere riproduzioni in modo facile e veloce.

Mostra il supporto per l'API

Intendi utilizzare l'API di riconoscimento della scrittura a mano libera? Il supporto pubblico aiuta il team di Chromium a dare priorità alle funzionalità e mostra agli altri fornitori di browser quanto sia fondamentale supportarle.

Condividi come intendi utilizzarlo nel thread del discorso di WicG. Invia un tweet a @ChromiumDev usando l'hashtag #HandwritingRecognition e facci sapere dove e come lo utilizzi.

Ringraziamenti

Questo articolo è stato scritto da Joe Medley, Honglin Yu e Jiewei Qian. Immagine hero di Samir Bouaked su Unsplash.