Reconhecer a escrita à mão dos usuários

A API de reconhecimento de escrita permite que você reconheça um texto de uma entrada escrita à mão enquanto ele acontece.

O que é a API de reconhecimento de escrita manual?

A API de reconhecimento de escrita permite que você converta a escrita à mão (tinta) de seus usuários em texto. Alguns sistemas operacionais já incluem essas APIs há muito tempo e, com esse novo recurso, seus aplicativos da web podem e, finalmente, usar essa funcionalidade. A conversão ocorre diretamente no dispositivo do usuário, funciona mesmo em modo off-line, sem adicionar bibliotecas ou serviços de terceiros.

Essa API implementa as chamadas "on-line" ou reconhecimento quase em tempo real. Isso significa que entrada escrita à mão é reconhecida enquanto o usuário a desenha capturando e analisando traços. Diferente de "off-line", procedimentos, como o reconhecimento óptico de caracteres (OCR), em que somente o produto final é conhecido, algoritmos on-line podem fornecer um nível maior de precisão devido à 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

Alguns exemplos de usos incluem:

  • Aplicativos de anotações em que os usuários querem capturar notas escritas à mão e traduzi-las em texto.
  • Aplicativos de formulários em que os usuários podem digitar com caneta ou dedo devido a restrições de tempo.
  • Jogos que exigem o preenchimento de letras ou números, como palavras cruzadas, forca ou sudoku.

Status atual

A API de reconhecimento de escrita está disponível no Chromium 99.

Como usar a API de reconhecimento de escrita manual

Detecção de recursos

Detectar a compatibilidade com o 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 converte entradas escritas à mão em texto, independentemente do método de entrada (mouse, toque, caneta). A API tem quatro entidades principais:

  1. Um ponto representa onde o ponteiro estava em um momento específico.
  2. Um traço consiste em um ou mais pontos. O registro 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 uma caneta ou e termina quando ele levanta o ponteiro novamente.
  3. Um desenho consiste em um ou mais traços. O reconhecimento real ocorre nesse nível.
  4. O reconhecedor 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 serão abordados em breve.

As principais entidades da API de reconhecimento de escrita manual: um ou mais pontos compõe um traço, um ou mais traços compõe um desenho criado pelo reconhecedor. O reconhecimento real ocorre no nível do desenho.

Como criar um reconhecedor

Para reconhecer texto de uma entrada manuscrita, você precisa obter uma instância de um HandwritingRecognizer chamando navigator.createHandwritingRecognizer() e transmitindo restrições. a ele. As restrições determinam o modelo de reconhecimento de escrita manual que deve ser usado. Atualmente, pode 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 a navegador pode atender à sua solicitação. Caso contrário, a promessa vai ser rejeitada com um erro. o reconhecimento de escrita manual não estará disponível. Por isso, convém consultar o o suporte do reconhecedor para recursos de reconhecimento específicos primeiro.

Consulta ao suporte do reconhecedor

Ao chamar navigator.queryHandwritingRecognizerSupport(), é possível verificar se a plataforma de destino suporta os recursos de reconhecimento de escrita manual que você pretende usar. No exemplo a seguir, desenvolvedor:

  • quer detectar textos em inglês
  • receba previsões alternativas, menos prováveis, quando disponíveis
  • obter acesso ao resultado da segmentação, ou seja, os caracteres reconhecidos, incluindo os pontos e traços que as 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, será definido como false. Você pode usar essas informações para ativar ou desativar determinados recursos no aplicativo ou para ajuste sua consulta e envie uma nova.

Iniciar um desenho

No seu aplicativo, você deve oferecer uma área de entrada onde o usuário faça algo escrito à mão de entradas de registro. Por motivos de desempenho, é recomendável implementar isso com a ajuda de um canvas_object. O exato implementação desta parte está fora do escopo deste artigo, mas você pode consultar os artigos de demonstração para ver como isso pode ser feito.

Para iniciar um novo desenho, chame o método startDrawing() no reconhecedor. Esse método recebe uma 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: entrada por 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 de reconhecimento de escrita funciona bem com Eventos de ponteiro, que fornecem uma interface abstrata para consumir entrada de qualquer dispositivo apontador. Os argumentos do evento ponteiro contêm o tipo de ponteiro 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 é automaticamente criado na primeira ocorrência de um evento pointerdown na área de escrita à mão. Conforme o pointerType pode estar vazio ou definido como um valor reservado, apresentamos uma verificação de consistência para garantir garantir que apenas 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', '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 isso, crie um novo instância de HandwritingStroke. Além disso, você deve armazenar a hora atual como um ponto de referência para os próximos pontos adicionados a ela:

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

Adicionar um ponto

Depois de criar o traço, adicione diretamente o primeiro ponto a ele. Conforme você adicionar mais mais adiante, faz sentido implementar a lógica de criação de pontos em um método separado. Na exemplo a seguir, o método addPoint() calcula o tempo decorrido a partir do carimbo de data/hora de referência. A informação temporal é opcional, mas pode melhorar a qualidade do reconhecimento. Depois, ele lê o X e Coordenadas Y do evento de 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 adicionadas ao traço. O evento também pode ser gerado se o ponteiro não estiver em um "baixo" estado, por exemplo, ao mover o cursor pela tela sem pressionar o mouse . O manipulador de eventos do exemplo a seguir verifica se existe um traço ativo e adiciona o um novo ponto para ela.

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 seu desenho chamando o addStroke(). O exemplo a seguir também redefine o activeStroke, de modo que o 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, então você pode executar previsões, 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 pelos probabilidade. O número de elementos depende do valor transmitido para a dica alternatives. Você poderia usar essa matriz para apresentar ao usuário várias opções de correspondências possíveis e fazer com que ele selecione uma é a melhor opção. Também é possível usar a previsão mais provável, que é o que faço no exemplo.

O objeto de previsão contém o texto reconhecido e um resultado de segmentação opcional, que vamos serão discutidos na próxima seção.

Insights detalhados com resultados de segmentação

Se compatível com a plataforma de destino, o objeto de previsão também pode conter um resultado de segmentação. Trata-se de uma matriz que contém todos os segmentos de escrita à mão reconhecidos, uma combinação das caractere identificável pelo usuário (grapheme) junto com sua 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 os grafemas reconhecidos na tela novamente.

As caixas são desenhadas em torno de cada grafema reconhecido

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 uma aprimorado progressivamente, controle de edição capaz de escrever à mão e reconhecimento de objetos. Ao clicar no botão no canto inferior direito do controle de edição, você ativa o modo de desenho. Ao concluir o desenho, o componente Web iniciará automaticamente o reconhecimento de imagem e adiciona o texto reconhecido de volta ao controle de edição. Se o reconhecimento de escrita manual A API não é compatível ou a plataforma não oferece suporte aos recursos solicitados, o botão de edição serão ocultados. Mas o controle básico de edição ainda pode ser usado como um <textarea>.

O componente Web oferece propriedades e atributos para definir o comportamento de reconhecimento do externas, incluindo languages e recognitiontype. É possível definir o conteúdo do controle por meio do Atributo value:

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

Para saber sobre alterações no valor, detecte o evento input.

Você pode testar o componente usando esta demonstração no Glitch (link em inglês). Confira também código-fonte. Para usar o controle na sua aplicativo, faça o download no npm.

Segurança e permissões

A equipe do Chromium projetou e implementou a API de reconhecimento manual usando os princípios básicos definido em Como controlar o acesso a recursos avançados da Web Platform, incluindo controle, transparência e ergonomia.

Controle do usuário

A API de reconhecimento de escrita manual não pode ser desativada pelo usuário. Ele só está disponível para sites. via HTTPS e só podem ser chamados a partir 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 o uso de técnicas de impressão digital, o navegador implementa contramedidas, como mostrar uma solicitação de permissão ao usuário quando ele detecta possível abuso.

Persistência da permissão

No momento, a API de reconhecimento de escrita manual não mostra solicitações de permissão. Assim, a permissão não precisa ser persistido.

Feedback

A equipe do Chromium quer saber mais sobre suas experiências com a API Handwriting Recognition.

Fale sobre o design da API

Alguma coisa na API não funciona como você esperava? Ou há métodos faltando ou propriedades de que precisa para implementar sua ideia? Tem uma pergunta ou comentário sobre a segurança modelo? Registre um problema de especificação no repositório do GitHub correspondente ou adicione sua opinião a uma problema.

Informar um problema com a implementação

Você encontrou um bug na implementação do Chromium? Ou a implementação é diferente das especificações? Registre um bug em new.crbug.com. Não deixe de incluir o máximo de detalhes possível, instruções simples para reprodução, e digite Blink>Handwriting na caixa Componentes. O Glitch é ótimo para compartilhar repetições rápidas e fáceis.

Mostrar suporte à API

Você planeja usar a API de reconhecimento de escrita manual? Seu apoio público ajuda a equipe do Chromium priorizar recursos e mostrar a outros fornecedores de navegador como é fundamental oferecer suporte a eles.

Compartilhe como você planeja usá-la na conversa sobre a WCG (em inglês). Enviar um tweet para @ChromiumDev usando a hashtag #HandwritingRecognition e informe onde e como você o utiliza.

Agradecimentos

Este artigo foi revisado por Joe Medley, Honglin Yu e Jiewei Qian. Imagem principal de Samir Bouaked no Abrir a página.