A API de reconhecimento de escrita manual permite reconhecer texto de entrada escrita à mão enquanto ele acontece.
O que é a API Handwriting Recognition?
A API de reconhecimento de escrita manual permite converter a escrita à mão (tinta) dos usuários em texto. Alguns sistemas operacionais já incluem essas APIs há muito tempo e, com esse novo recurso, seus apps da Web podem finalmente usar essa funcionalidade. A conversão ocorre diretamente no dispositivo do usuário e funciona até mesmo no modo off-line, sem a necessidade de adicionar bibliotecas ou serviços de terceiros.
Essa API implementa o reconhecimento chamado "on-line" ou quase em tempo real. Isso significa que a entrada escrita à mão é reconhecida enquanto o usuário a desenha, capturando e analisando os traços únicos. Ao contrário de procedimentos "off-line", como reconhecimento óptico de caracteres (OCR), em que apenas o produto final é conhecido, os algoritmos on-line podem fornecer um nível mais alto de precisão devido a outros sinais, 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 usos incluem:
- Aplicativos de anotações em que os usuários querem capturar notas escritas à mão e fazer com que elas sejam traduzidas em texto.
- Aplicativos de formulários em que os usuários podem inserir entrada com caneta ou dedo devido a restrições de tempo.
- Jogos que exigem o preenchimento de letras ou números, como palavras cruzadas, travesseiro 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 Navigator:
if ('createHandwritingRecognizer' in navigator) {
// 🎉 The Handwriting Recognition API is supported!
}
Principais conceitos
A API de reconhecimento de escrita manual converte entradas manuscritas em texto, independentemente do método de entrada (mouse, toque, caneta). 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 caneta ou o dedo) e termina quando ele o levanta.
- Um desenho consiste em um ou mais traços. O reconhecimento real ocorre nesse nível.
- O Reconhecer está 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 abordaremos em breve.
Como criar um reconhecedor
Para reconhecer texto de entrada manuscrita, você precisa conseguir uma instância de um
HandwritingRecognizer
chamando navigator.createHandwritingRecognizer()
e transmitindo restrições
a ela. As restrições determinam o modelo de reconhecimento de escrita manual que precisa ser usado. Atualmente, é 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 que resolve com uma instância de HandwritingRecognizer
quando o navegador pode atender à sua solicitação. Caso contrário, ele rejeitará a promessa com um erro, e o reconhecimento de escrita manual não ficará disponível. Por esse motivo, consulte primeiro o suporte do reconhecedor para recursos de reconhecimento específicos.
Como consultar o suporte do reconhecedor
Ao chamar navigator.queryHandwritingRecognizerSupport()
, é possível verificar se a plataforma de destino
é compatível com os recursos de reconhecimento de escrita manual que você pretende usar. No exemplo a seguir, o
desenvolvedor:
- quer detectar textos em inglês
- receber previsões alternativas e menos prováveis quando disponíveis
- acessar o resultado da segmentação, ou seja, os caracteres reconhecidos, incluindo os pontos e traços que os compõem.
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
O método retorna uma promessa que é resolvida com um objeto de resultado. Se o navegador for compatível com o recurso
especificado pelo desenvolvedor, o valor dele será definido como true
. Caso contrário, ele é definido como false
.
É possível usar essas informações para ativar ou desativar determinados recursos no app ou para
ajustar sua consulta e enviar uma nova.
Iniciar um desenho
No seu aplicativo, ofereça uma área de entrada em que o usuário faz entradas manuscritas. Para melhorar o desempenho, recomendamos fazer essa implementação 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 ver como fazer isso.
Para iniciar um novo desenho, chame o método startDrawing()
no reconhecedor. Esse método usa um objeto que contém dicas diferentes 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 caneta (
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 entrada de qualquer dispositivo apontador. Os argumentos do evento de ponteiro contêm
o tipo de ponteiro que está sendo usado. Isso significa que é possível usar eventos de ponteiro para determinar o tipo de entrada
automaticamente. No exemplo a seguir, o desenho para reconhecimento de escrita manual é criado automaticamente
na primeira ocorrência de um evento pointerdown
na área de escrita à mão. Como
pointerType
pode estar vazio ou definido como um valor reservado, introduzi uma verificação de consistência para garantir
que apenas os valores com suporte 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', 'pen'].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, é preciso armazenar o horário atual como 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ê adicionará mais pontos posteriormente, 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 manipulador de eventos pointermove
é chamado quando o ponteiro é movido pela tela. Esses pontos também
precisam ser adicionados ao traço. O evento também poderá ser acionado se o ponteiro não estiver no
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 existe um traço ativo e adiciona o novo ponto a ele.
canvas.addEventListener('pointermove', (event) => {
if (activeStroke) {
addPoint(event);
}
});
Reconhecer texto
Quando o usuário levantar o ponteiro novamente, você poderá adicionar o traço ao desenho chamando o
método addStroke()
. O exemplo a seguir também redefine o activeStroke
. Portanto, o gerenciador pointermove
não adicionará 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 do que algumas centenas de milissegundos. Assim, 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 pela probabilidade. O número de elementos depende do valor transmitido para a dica alternatives
. Você
pode usar essa matriz para apresentar ao usuário uma escolha de possíveis correspondências e para que ele selecione uma
opção. Outra opção é usar a previsão mais provável, que é o que fiz 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 for compatível com a plataforma de destino, o objeto de previsão também poderá conter um resultado de segmentação.
Essa é 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 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 novamente os grafemas reconhecidos na tela.
Reconhecimento completo
Após a conclusão do reconhecimento, é possível 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 que pode reconhecer a escrita
à mão. Para ativar o modo de desenho, clique no botão no canto inferior direito do controle de edição. Quando você concluir 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 de reconhecimento
de escrita manual não tiver suporte ou a plataforma não oferecer suporte aos recursos solicitados, o botão de edição
ficará oculto. No entanto, o controle básico de edição ainda pode ser usado como um <textarea>
.
O componente da Web oferece propriedades e atributos para definir o comportamento de reconhecimento externo, incluindo languages
e recognitiontype
. É possível definir o conteúdo do controle usando o
atributo value
:
<handwriting-textarea languages="en" recognitiontype="text" value="Hello"></handwriting-textarea>
Para ser informado sobre qualquer mudança no valor, detecte o evento input
.
Você pode testar o componente usando esta demonstração no Glitch. Além disso, confira o código-fonte. Para usar o controle no seu aplicativo, solicite-o no npm.
Segurança e permissões
A equipe do Chromium projetou e implementou a API de reconhecimento de escrita manual usando os princípios fundamentais definidos em Como controlar o acesso a recursos avançados da plataforma da Web, incluindo controle do usuário, transparência e ergonomia.
Controle do usuário
A API de reconhecimento de escrita manual não pode ser desativada pelo usuário. Ela só está disponível para sites enviados via HTTPS e só pode ser chamada no 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 o uso de técnicas de impressão digital, o navegador implementa medidas corretivas, como mostrar um aviso de permissão ao usuário quando detectar um possível abuso.
Persistência da permissão
No momento, a API Handwriting Recognition não mostra nenhum prompt de permissão. Portanto, a permissão não precisa ser mantida de nenhuma forma.
Feedback
A equipe do Chromium quer saber mais sobre suas experiências com a API de reconhecimento de escrita manual.
Fale sobre o design da API
Há algo na API que não funciona como você esperava? Ou faltam métodos ou propriedades para implementar sua ideia? Tem alguma dúvida ou comentário sobre o modelo de segurança? Registre um problema de especificação no repositório do GitHub correspondente ou adicione suas ideias a um problema atual.
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 insira Blink>Handwriting
na caixa Componentes.
O Glitch funciona muito bem para compartilhar repetições rápidas e fáceis.
Mostre seu suporte à API
Você planeja usar a API Handwriting Recognition? Seu suporte público ajuda a equipe do Chromium a priorizar recursos e mostra a outros fornecedores de navegadores como é fundamental oferecer suporte a eles.
Compartilhe como você planeja usá-lo na conversa do discurso do WiCG. Envie um tweet para
@ChromiumDev usando a hashtag
#HandwritingRecognition
e informe onde e como ela está sendo usada.
Links úteis
- Explicação
- Rascunho de especificações
- Repositório do GitHub
- ChromeStatus
- Bug do Chromium
- Revisão da TAG
- Intenção de criar o protótipo
- Conversa do WebKit-Dev
- Posição de padrão do Mozilla
Agradecimentos
Este artigo foi revisado por Joe Medley, Honglin Yu e Jiewei Qian. Imagem principal de Samir Bouaked no Unsplash (links em inglês).