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 Handwriting Recognition?

L'API Handwriting Recognition consente di convertire la scrittura a mano libera (inchiostro) degli utenti in testo. Alcuni sistemi operativi includono da tempo queste API e, con questa nuova funzionalità, le tue app web possono finalmente utilizzarle. 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 "on-line" o quasi in tempo reale. Ciò significa che l'input scritto a mano viene riconosciuto mentre l'utente lo disegna acquisendo e analizzando le singole tratti. A differenza delle procedure "off-line" come il riconoscimento ottico dei caratteri (OCR), in cui è noto solo il prodotto finale, gli algoritmi on-line possono fornire un livello di accuratezza superiore grazie a indicatori aggiuntivi come la sequenza temporale e la pressione dei singoli tratti di inchiostro.

Casi d'uso suggeriti per l'API Handwriting Recognition

Esempi di utilizzo includono:

  • Applicazioni per prendere appunti in cui gli utenti vogliono acquisire note scritte a mano e tradurle in testo.
  • Applicazioni di moduli in cui gli utenti possono utilizzare l'input con la penna o il dito a causa di vincoli di tempo.
  • Giochi che richiedono di inserire lettere o numeri, come cruciverba, gioco dell'impiccato o sudoku.

Stato attuale

L'API Handwriting Recognition è disponibile a partire da Chromium 99.

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

Rilevamento di funzionalità

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 Handwriting Recognition converte l'input scritto a mano in testo, indipendentemente dal metodo di inserimento (mouse, tocco, penna). L'API ha quattro entità principali:

  1. Un punto rappresenta la posizione del cursore in un determinato momento.
  2. Un tratto è costituito da uno o più punti. La registrazione di un tratto inizia quando l'utente abbassa il cursore (ad es. fa clic sul pulsante principale del mouse o tocca lo schermo con la penna o il dito) e termina quando lo solleva di nuovo.
  3. Un disegno è costituito da una o più tratti. Il riconoscimento effettivo avviene a questo livello.
  4. La funzione di riconoscimento è configurata con la lingua di inserimento prevista. Viene utilizzato per creare un'istanza di un disegno con la configurazione del riconoscitore applicata.

Questi concetti sono implementati come interfacce e dizionari specifici, che tratterò a breve.

Le entità di base 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 riconoscitore. Il riconoscimento effettivo avviene a livello di disegno.

Creazione di un riconoscimento

Per riconoscere il testo dall'input scritto a mano, devi ottenere un'istanza di HandwritingRecognizer chiamando navigator.createHandwritingRecognizer() e passando i 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 si risolve con un'istanza di HandwritingRecognizer quando il browser può 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 rivolgerti innanzitutto all'assistenza del sistema di riconoscimento per determinate funzionalità 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 a mano libera che intendi utilizzare. Nell'esempio seguente, lo sviluppatore:

  • Vuole rilevare testi in inglese
  • ottenere previsioni alternative, meno probabili, se disponibili
  • accedere al risultato della segmentazione, ovvero ai caratteri riconosciuti, inclusi i punti e le linee 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 relativo valore verrà impostato su true. In caso contrario, verrà impostato su false. Puoi utilizzare queste informazioni per attivare o disattivare determinate funzionalità all'interno della tua applicazione oppure per modificare la query e inviarne una nuova.

Iniziare un disegno

All'interno dell'applicazione, dovresti offrire un'area di inserimento in cui l'utente effettua le proprie voci scritte a mano. Per motivi di prestazioni, è consigliabile implementare questa funzionalità con l'aiuto di un oggetto canvas. L'implementazione esatta di questa parte non rientra nell'ambito di questo articolo, ma puoi consultare la demo per scoprire come si può fare.

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

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

L'API di riconoscimento della scrittura a mano libera è compatibile con gli eventi del cursore, che forniscono un'interfaccia astratta per utilizzare l'input di qualsiasi dispositivo di puntamento. Gli argomenti dell'evento del cursore contengono il tipo di cursore 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 può essere vuoto o impostato su un valore proprietario, ho introdotto un controllo di coerenza per assicurarmi che per il tipo di input del disegno vengano impostati solo i valori supportati.

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

Aggiungere un tratto

L'evento pointerdown è anche il punto 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);
}

Aggiunge un punto

Dopo aver creato il tratto, devi aggiungere direttamente il primo punto. Poiché aggiungerai altri punti in un secondo momento, 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 temporali sono facoltative, ma possono migliorare la qualità del riconoscimento. Quindi, legge le coordinate X e Y dall'evento del cursore 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. Questi punti devono essere aggiunti anche 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 nell'esempio seguente controlla se esiste un tratto attivo e 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(). L'esempio seguente reimposta anche activeStroke, pertanto l'handler pointermove non aggiungerà punti al tratto completato.

A questo punto, è il momento di riconoscere l'input dell'utente chiamando il metodo getPrediction() sul disegno. Il riconoscimento di solito richiede meno di qualche centinaio di millisecondi, quindi puoi eseguire ripetutamente le predizioni, 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 loro 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 semplicemente utilizzare la previsione più probabile, che è ciò che faccio nell'esempio.

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

Approfondimenti dettagliati con i risultati della 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.

Vengono disegnate caselle intorno a ogni grafema riconosciuto

Riconoscimento completo

Al termine del riconoscimento, puoi liberare le 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à di disegno. Al termine del disegno, il componente web avvia automaticamente il riconoscimento e aggiunge 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 essere informato di 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 potenti funzionalità della piattaforma web, tra cui il controllo dell'utente, la trasparenza e l'ergonomia.

Controllo utente

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 è presente alcuna indicazione se 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 delle autorizzazioni

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

Feedback

Il team di Chromium vuole conoscere la tua esperienza con l'API Handwriting Recognition.

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 una segnalazione relativa alle specifiche nel repository GitHub corrispondente o aggiungi il tuo parere a un problema esistente.

Segnala un problema con l'implementazione

Hai trovato un bug nell'implementazione di Chromium? Oppure l'implementazione è diversa dalla specifica? 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 è ideale per condividere riproduzioni rapide e semplici.

Mostra il supporto per l'API

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

Spiega come prevedi di utilizzarlo nel thread di Discourse del WICG. Invia un tweet a @ChromiumDev utilizzando l'hashtag #HandwritingRecognition e facci sapere dove e come lo utilizzi.

Ringraziamenti

Questo documento è stato esaminato da Joe Medley, Honglin Yu e Jiewei Qian.