A API Handwriting Recognition permite reconhecer o texto de uma entrada escrita à mão em tempo real.
O que é a API Handwriting Recognition?
A API Handwriting Recognition permite converter a escrita à mão (tinta) dos usuários em texto. Alguns sistemas operacionais já incluíam essas APIs há muito tempo. Com esse novo recurso, seus apps da Web podem finalmente usar essa funcionalidade. A conversão ocorre diretamente no dispositivo do usuário, funciona mesmo no modo off-line e tudo isso sem adicionar bibliotecas ou serviços de terceiros.
Essa API implementa o chamado reconhecimento "on-line" ou quase em tempo real. Isso significa que a entrada manuscrita é reconhecida enquanto o usuário a desenha, capturando e analisando os traços individuais. Em contraste com os procedimentos "off-line", como o reconhecimento óptico de caracteres (OCR, na sigla em inglês), em que apenas o produto final é conhecido, os algoritmos on-line podem fornecer um nível mais alto de precisão devido a sinais adicionais, como a sequência temporal e a pressão de traços de tinta individuais.
Casos de uso sugeridos para a API Handwriting Recognition
Exemplos de uso incluem:
- Aplicativos de anotações em que os usuários querem capturar notas manuscritas e convertê-las em texto.
- Formulários em que os usuários podem usar a stylus ou a entrada por dedo devido a restrições de tempo.
- Jogos que exigem o preenchimento de letras ou números, como palavras cruzadas, jogo da forca ou sudoku.
Status atual
A API Handwriting Recognition está disponível no Chromium 99.
Como usar a API Handwriting Recognition
Detecção de recursos
Detecte o suporte do navegador verificando a existência do método createHandwritingRecognizer()
no objeto de navegação:
if ('createHandwritingRecognizer' in navigator) {
// 🎉 The Handwriting Recognition API is supported!
}
Principais conceitos
A API Handwriting Recognition converte a entrada manuscrita em texto, independentemente do método de entrada (mouse, toque, stylus). A API tem quatro entidades principais:
- Um ponto representa onde o ponteiro estava em um determinado momento.
- Um traço consiste em um ou mais pontos. A gravação de um traço começa quando o usuário coloca o ponteiro para baixo (ou seja, clica no botão principal do mouse ou toca na tela com a stylus ou o dedo) e termina quando ele levanta o ponteiro novamente.
- Um desenho consiste em um ou mais traços. O reconhecimento real ocorre nesse nível.
- O reconhecedor é configurado com o idioma de entrada esperado. Ele é usado para criar uma instância de um desenho com a configuração do reconhecedor aplicada.
Esses conceitos são implementados como interfaces e dicionários específicos, que vou abordar em breve.
Criar um reconhecedor
Para reconhecer texto de uma entrada manuscrita, é necessário chamar navigator.createHandwritingRecognizer()
e transmitir restrições
a uma instância de HandwritingRecognizer
. As restrições determinam o modelo de reconhecimento de escrita manual que deve ser usado. No momento, é possível
especificar uma lista de idiomas em ordem de preferência:
const recognizer = await navigator.createHandwritingRecognizer({
languages: ['en'],
});
O método retorna uma promessa resolvida com uma instância de um HandwritingRecognizer
quando o
navegador pode atender à solicitação. Caso contrário, a promessa será rejeitada com um erro, e
o reconhecimento de escrita à mão não estará disponível. Por esse motivo, é recomendável consultar o
suporte do reconhecedor para recursos de reconhecimento específicos.
Consultar o suporte do reconhecedor
Ao chamar navigator.queryHandwritingRecognizer()
, você pode verificar se a plataforma de destino
oferece suporte aos recursos de reconhecimento de escrita à mão que você pretende usar. Esse método usa
o mesmo objeto de restrição do método navigator.createHandwritingRecognizer()
,
contendo a lista de idiomas solicitados. O método retorna uma promessa resolvida
com um objeto de resultado se um reconhecedor compatível for encontrado. Caso contrário, a promessa é resolvida como null
.
No exemplo abaixo, o desenvolvedor:
- quer detectar textos em inglês
- receber previsões alternativas menos prováveis quando disponíveis
- ter acesso ao resultado da segmentação, ou seja, os caracteres reconhecidos, incluindo os pontos e os traços que os compõem
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
Se o navegador oferecer suporte ao recurso
necessário para o desenvolvedor, o valor dele será definido como true
no objeto de resultado. Caso contrário, será definido como false
.
Você pode usar essas informações para ativar ou desativar determinados recursos no seu aplicativo ou enviar uma nova consulta para um conjunto diferente de idiomas.
Começar a desenhar
No app, ofereça uma área de entrada em que o usuário faça anotações escritas à mão. Por motivos de desempenho, é recomendável implementar isso com a ajuda de um objeto de tela. A implementação exata desta parte está fora do escopo deste artigo, mas você pode consultar a demonstração para saber como fazer isso.
Para iniciar um novo desenho, chame o método startDrawing()
no reconhecedor. Esse método usa um
objeto com diferentes dicas para ajustar o algoritmo de reconhecimento. Todas as dicas são opcionais:
- O tipo de texto inserido: texto, endereços de e-mail, números ou um caractere individual
(
recognitionType
) - O tipo de dispositivo de entrada: mouse, toque ou entrada por stylus (
inputType
). - O texto anterior (
textContext
) - O número de previsões alternativas menos prováveis que precisam ser retornadas (
alternatives
) - Uma lista de caracteres identificáveis pelo usuário ("grafemas") que o usuário provavelmente vai inserir
(
graphemeSet
)
A API Handwriting Recognition funciona bem com
eventos de ponteiro, que fornecem uma
interface abstrata para consumir entradas de qualquer dispositivo de ponteiro. Os argumentos do evento do ponteiro contêm
o tipo de ponteiro que está sendo usado. Isso significa que você pode usar eventos de ponteiro para determinar o tipo de entrada
automaticamente. No exemplo abaixo, o desenho para reconhecimento de escrita manual é criado automaticamente
na primeira ocorrência de um evento pointerdown
na área de escrita manual. Como o
pointerType
pode estar vazio ou definido como um valor reservado, introduzi uma verificação de consistência para garantir
que apenas os valores compatíveis sejam definidos para o tipo de entrada do desenho.
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);
});
Adicionar um traço
O evento pointerdown
também é o lugar certo para iniciar um novo traço. Para fazer isso, crie uma nova
instância de HandwritingStroke
. Além disso, você precisa armazenar o horário atual como um ponto de referência para
os pontos subsequentes adicionados a ele:
function startStroke(event) {
activeStroke = {
stroke: new HandwritingStroke(),
startTime: Date.now(),
};
addPoint(event);
}
Adicionar um ponto
Depois de criar o traço, adicione o primeiro ponto diretamente a ele. Como você vai adicionar mais
pontos mais tarde, faz sentido implementar a lógica de criação de pontos em um método separado. No
exemplo abaixo, o método addPoint()
calcula o tempo decorrido a partir do carimbo de data/hora de referência.
As informações temporais são opcionais, mas podem melhorar a qualidade do reconhecimento. Em seguida, ele lê as coordenadas X e
Y do evento do ponteiro e adiciona o ponto ao traço atual.
function addPoint(event) {
const timeElapsed = Date.now() - activeStroke.startTime;
activeStroke.stroke.addPoint({
x: event.offsetX,
y: event.offsetY,
t: timeElapsed,
});
}
O gerenciador de eventos pointermove
é chamado quando o ponteiro é movido pela tela. Esses pontos
também precisam ser adicionados ao traço. O evento também pode ser gerado se o ponteiro não estiver em um
estado "para baixo", por exemplo, ao mover o cursor pela tela sem pressionar o botão do
mouse. O manipulador de eventos do exemplo a seguir verifica se um traço ativo existe e adiciona o
novo ponto a ele.
canvas.addEventListener('pointermove', (event) => {
if (activeStroke) {
addPoint(event);
}
});
Reconhecer texto
Quando o usuário levanta o ponteiro novamente, você pode adicionar o traço ao desenho chamando o
método addStroke()
. O exemplo a seguir também redefine a activeStroke
, para que o gerenciador pointermove
não adicione pontos ao traço concluído.
Em seguida, é hora de reconhecer a entrada do usuário chamando o método getPrediction()
no
desenho. O reconhecimento geralmente leva menos de algumas centenas de milissegundos, então você pode executar previsões repetidamente, se necessário. O exemplo a seguir executa uma nova previsão após cada traço concluído.
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));
});
Esse método retorna uma promessa que é resolvida com uma matriz de previsões ordenadas por
probabilidade. O número de elementos depende do valor transmitido para a sugestão alternatives
. Você
pode usar essa matriz para apresentar ao usuário uma escolha de possíveis correspondências e fazer com que ele selecione uma
opção. Como alternativa, você pode usar a previsão mais provável, que é o que eu faço no
exemplo.
O objeto de previsão contém o texto reconhecido e um resultado de segmentação opcional, que será discutido na próxima seção.
Insights detalhados com resultados de segmentação
Se a plataforma de destino oferecer suporte, o objeto de previsão também poderá conter um resultado de segmentação.
É uma matriz que contém todos os segmentos de escrita à mão reconhecidos, uma combinação do caractere
identificável pelo usuário (grapheme
) com a posição dele no texto reconhecido
(beginIndex
, endIndex
) e os traços e pontos que o criaram.
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);
});
},
);
}
Você pode usar essas informações para rastrear os grafemas reconhecidos na tela novamente.
Reconhecimento completo
Depois que o reconhecimento for concluído, você poderá liberar recursos chamando o método clear()
no
HandwritingDrawing
e o método finish()
no HandwritingRecognizer
:
drawing.clear();
recognizer.finish();
Demonstração
O componente da Web <handwriting-textarea>
implementa um
controle de edição aprimorado progressivamente, capaz de reconhecer a
escrita manual. Ao clicar no botão no canto inferior direito do controle de edição, você ativa
o modo de desenho. Quando você terminar o desenho, o componente da Web vai iniciar automaticamente o
reconhecimento e adicionar o texto reconhecido de volta ao controle de edição. Se a API Handwriting Recognition
não tiver suporte ou a plataforma não oferecer suporte aos recursos solicitados, o botão de edição
será ocultado. Mas o controle de edição básico continua utilizável como <textarea>
.
O componente da Web oferece propriedades e atributos para definir o comportamento de reconhecimento de fora, incluindo languages
e recognitiontype
. É possível definir o conteúdo do controle pelo
atributo value
:
<handwriting-textarea languages="en" recognitiontype="text" value="Hello"></handwriting-textarea>
Para receber notificações sobre mudanças no valor, detecte o evento input
.
Teste o componente usando esta demonstração no Glitch. Confira também o código-fonte. Para usar o controle no aplicativo, faça o download dele no npm.
Segurança e permissões
A equipe do Chromium projetou e implementou a API Handwriting Recognition usando os princípios básicos definidos em Como controlar o acesso a recursos poderosos da plataforma da Web, incluindo controle do usuário, transparência e ergonomia.
Controle do usuário
A API Handwriting Recognition não pode ser desativada pelo usuário. Ele está disponível apenas para sites entregues por HTTPS e só pode ser chamado do contexto de navegação de nível superior.
Transparência
Não há indicação de que o reconhecimento de escrita manual está ativo. Para evitar a impressão digital, o navegador implementa contramedidas, como mostrar uma solicitação de permissão ao usuário quando detecta possível abuso.
Persistência de permissões
No momento, a API Handwriting Recognition não mostra nenhuma solicitação de permissão. Portanto, a permissão não precisa ser mantida de nenhuma forma.
Feedback
A equipe do Chromium quer saber sobre sua experiência com a API Handwriting Recognition.
Conte sobre o design da API
Há algo na API que não funciona como esperado? Ou há métodos ou propriedades ausentes que você precisa implementar para implementar sua ideia? Tem dúvidas ou comentários sobre o modelo de segurança? Envie um problema de especificação no repositório do GitHub correspondente ou adicione sua opinião a um problema existente.
Informar um problema com a implementação
Você encontrou um bug na implementação do Chromium? Ou a implementação é diferente da especificação?
Registre um bug em new.crbug.com. Inclua o máximo de detalhes possível,
instruções simples para reprodução e digite Blink>Handwriting
na caixa Components.
Mostrar suporte para a API
Você pretende usar a API Handwriting Recognition? Seu apoio público ajuda a equipe do Chromium a priorizar recursos e mostra a outros fornecedores de navegadores a importância de oferecer suporte a eles.
Compartilhe como você planeja usá-lo na discussão do Discourse do WICG. Envie um tweet para
@ChromiumDev usando a hashtag
#HandwritingRecognition
e nos informe onde e como você está usando.
Links úteis
- Explicação
- Especificação do rascunho
- Repositório do GitHub
- ChromeStatus
- Bug do Chromium
- Análise da TAG
- Da intenção ao protótipo
- Discussão do WebKit-Dev
- Posição dos padrões do Mozilla
Agradecimentos
Este documento foi revisado por Joe Medley, Honglin Yu e Jiewei Qian.