Com o reconhecimento de tinta digital do Kit de ML, é possível reconhecer texto escrito à mão em uma superfície digital em centenas de idiomas, bem como classificar esboços.
Faça um teste
- Teste o app de exemplo para um exemplo de uso dessa API.
Antes de começar
- No arquivo
build.gradle
no nível do projeto, inclua o repositório Maven do Google nas seçõesbuildscript
eallprojects
. - Adicione as dependências das bibliotecas do Android do Kit de ML ao arquivo Gradle no nível do app do módulo, que geralmente é
app/build.gradle
:
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:18.1.0'
}
Agora está tudo pronto para você reconhecer texto em objetos Ink
.
Criar um objeto Ink
A principal maneira de criar um objeto Ink
é desenhá-lo em uma tela touchscreen. Ativado
no Android, é possível usar
Canvas para
para esse propósito. Seu
manipuladores de evento de toque
precisa chamar addNewTouchEvent()
mostrou o seguinte snippet de código para armazenar os pontos nos traços que
que o usuário desenha no objeto Ink
.
Esse padrão geral é demonstrado no snippet de código a seguir. Consulte a Amostra do guia de início rápido do Kit de ML para conferir um exemplo mais completo.
Kotlin
var inkBuilder = Ink.builder() lateinit var strokeBuilder: Ink.Stroke.Builder // Call this each time there is a new event. fun addNewTouchEvent(event: MotionEvent) { val action = event.actionMasked val x = event.x val y = event.y var t = System.currentTimeMillis() // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create when (action) { MotionEvent.ACTION_DOWN -> { strokeBuilder = Ink.Stroke.builder() strokeBuilder.addPoint(Ink.Point.create(x, y, t)) } MotionEvent.ACTION_MOVE -> strokeBuilder!!.addPoint(Ink.Point.create(x, y, t)) MotionEvent.ACTION_UP -> { strokeBuilder.addPoint(Ink.Point.create(x, y, t)) inkBuilder.addStroke(strokeBuilder.build()) } else -> { // Action not relevant for ink construction } } } ... // This is what to send to the recognizer. val ink = inkBuilder.build()
Java
Ink.Builder inkBuilder = Ink.builder(); Ink.Stroke.Builder strokeBuilder; // Call this each time there is a new event. public void addNewTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); long t = System.currentTimeMillis(); // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: strokeBuilder = Ink.Stroke.builder(); strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_MOVE: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_UP: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); inkBuilder.addStroke(strokeBuilder.build()); strokeBuilder = null; break; } } ... // This is what to send to the recognizer. Ink ink = inkBuilder.build();
Acessar uma instância do DigitalInk Reconhecedor
Para realizar o reconhecimento, envie a instância Ink
para um
objeto DigitalInkRecognizer
. O código abaixo mostra como instanciar esse
de uma tag BCP-47.
Kotlin
// Specify the recognition model for a language var modelIdentifier: DigitalInkRecognitionModelIdentifier try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US") } catch (e: MlKitException) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } var model: DigitalInkRecognitionModel = DigitalInkRecognitionModel.builder(modelIdentifier).build() // Get a recognizer for the language var recognizer: DigitalInkRecognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build())
Java
// Specify the recognition model for a language DigitalInkRecognitionModelIdentifier modelIdentifier; try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US"); } catch (MlKitException e) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } DigitalInkRecognitionModel model = DigitalInkRecognitionModel.builder(modelIdentifier).build(); // Get a recognizer for the language DigitalInkRecognizer recognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build());
Processar um objeto Ink
Kotlin
recognizer.recognize(ink) .addOnSuccessListener { result: RecognitionResult -> // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. Log.i(TAG, result.candidates[0].text) } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error during recognition: $e") }
Java
recognizer.recognize(ink) .addOnSuccessListener( // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. result -> Log.i(TAG, result.getCandidates().get(0).getText())) .addOnFailureListener( e -> Log.e(TAG, "Error during recognition: " + e));
O exemplo de código acima pressupõe que o modelo de reconhecimento já foi transferidos por download, conforme descrito na próxima seção.
Como gerenciar downloads de modelos
Embora a API de reconhecimento de tinta digital ofereça suporte a centenas de idiomas, cada
idioma exige que alguns dados sejam baixados antes de qualquer reconhecimento. Por volta de
São necessários 20 MB de armazenamento por idioma. Isso é gerenciado pela
RemoteModelManager
.
Fazer o download de um novo modelo
Kotlin
import com.google.mlkit.common.model.DownloadConditions import com.google.mlkit.common.model.RemoteModelManager var model: DigitalInkRecognitionModel = ... val remoteModelManager = RemoteModelManager.getInstance() remoteModelManager.download(model, DownloadConditions.Builder().build()) .addOnSuccessListener { Log.i(TAG, "Model downloaded") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while downloading a model: $e") }
Java
import com.google.mlkit.common.model.DownloadConditions; import com.google.mlkit.common.model.RemoteModelManager; DigitalInkRecognitionModel model = ...; RemoteModelManager remoteModelManager = RemoteModelManager.getInstance(); remoteModelManager .download(model, new DownloadConditions.Builder().build()) .addOnSuccessListener(aVoid -> Log.i(TAG, "Model downloaded")) .addOnFailureListener( e -> Log.e(TAG, "Error while downloading a model: " + e));
Verificar se um modelo já foi baixado
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
Excluir um modelo baixado
Remover um modelo do armazenamento do dispositivo libera espaço.
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener { Log.i(TAG, "Model successfully deleted") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while deleting a model: $e") }
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener( aVoid -> Log.i(TAG, "Model successfully deleted")) .addOnFailureListener( e -> Log.e(TAG, "Error while deleting a model: " + e));
Dicas para melhorar a precisão do reconhecimento de texto
A precisão do reconhecimento de texto pode variar de acordo com o idioma. A precisão também depende sobre o estilo de escrita. Embora o reconhecimento de tinta digital seja treinado para lidar com muitos tipos de estilos de escrita, os resultados podem variar de usuário para usuário.
Aqui estão algumas maneiras de melhorar a precisão de um reconhecedor de texto. Observe que essas técnicas não se aplicam aos classificadores de desenho para emojis, desenho automático e formas.
Área de escrita
Muitos aplicativos têm uma área de gravação bem definida para entradas do usuário. O significado de um símbolo é parcialmente determinada por seu tamanho em relação ao tamanho da área de escrita que a contém. Por exemplo, a diferença entre uma letra "o" minúscula ou uma letra maiúscula. ou "c", e uma vírgula versus barra para a direita.
Dizer ao reconhecedor a largura e a altura da área de escrita pode melhorar a precisão. No entanto, o reconhecedor presume que a área de escrita contém apenas uma linha de texto. Se o ponto físico área de escrita for grande o suficiente para permitir que o usuário escreva duas ou mais linhas, talvez seja melhor resultados passando um WritingArea com uma altura que seja a melhor estimativa da altura de um uma única linha de texto. O objeto WritingArea que você passa para o reconhecedor não precisa corresponder exatamente com a área de escrita física na tela. Como alterar a altura da área de escrita dessa forma funciona melhor em alguns idiomas do que em outros.
Ao especificar a área de escrita, especifique a largura e a altura nas mesmas unidades que o traço. coordenadas. Os argumentos de coordenadas x,y não têm requisito de unidade. A API normaliza todos o que importa é o tamanho relativo e a posição dos traços. Você é livre para e passar coordenadas em qualquer escala que faça sentido para o seu sistema.
Pré-contexto
Pré-contexto é o texto que precede imediatamente os traços no Ink
que você
estão tentando reconhecer. Você pode ajudar o reconhecedor contando sobre o pré-contexto.
Por exemplo, as letras cursivas "n" e "u" muitas vezes são confundidos uns com os outros. Se o usuário tiver já inseriram a palavra parcial "arg", eles podem continuar com traços que podem ser reconhecidos como “umentá” ou "nment". Como especificar o "arg" de pré-contexto resolve a ambiguidade, já que a palavra "argumento" é mais provável que "argnamento".
O pré-contexto também pode ajudar o reconhecedor a identificar quebras de palavras, os espaços entre palavras. Você pode digite um caractere de espaço, mas não desenhe um, então como um reconhecedor pode determinar quando uma palavra termina e o próximo começa? Se o usuário já tiver escrito "hello" e continua com a palavra escrita "world", sem pré-contexto, o reconhecedor retorna a string "world". No entanto, se você especificar pré-contexto "hello", o modelo retornará a string " mundo", com um espaço na liderança, já que "olá" mundo" faz mais sentido do que "helloword".
Você deve fornecer a maior string de pré-contexto possível, com até 20 caracteres, incluindo espaços Se a string for maior, o reconhecedor usará apenas os últimos 20 caracteres.
O exemplo de código abaixo mostra como definir uma área de escrita e usar uma
Objeto RecognitionContext
para especificar o pré-contexto.
Kotlin
var preContext : String = ...; var width : Float = ...; var height : Float = ...; val recognitionContext : RecognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(WritingArea(width, height)) .build() recognizer.recognize(ink, recognitionContext)
Java
String preContext = ...; float width = ...; float height = ...; RecognitionContext recognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(new WritingArea(width, height)) .build(); recognizer.recognize(ink, recognitionContext);
Ordenação dos traços
A precisão do reconhecimento depende da ordem dos traços. Os reconhecedores esperam que os traços ocorrer na ordem em que as pessoas escreveriam naturalmente; por exemplo, da esquerda para a direita, no caso do inglês. Qualquer caso que se desvie desse padrão, como escrever uma frase começando com a última palavra, fornece resultados menos precisos.
Outro exemplo é quando uma palavra no meio de uma Ink
é removida e substituída por
outra palavra. A revisão provavelmente está no meio de uma frase, mas os traços da revisão
estão no final da sequência de traços.
Nesse caso, recomendamos enviar a nova palavra escrita separadamente para a API e mesclar o
com os reconhecimentos anteriores usando sua própria lógica.
Como lidar com formas ambíguas
Há casos em que o significado da forma fornecida ao reconhecedor é ambíguo. Para exemplo, um retângulo com bordas muito arredondadas pode ser visto como um retângulo ou uma elipse.
Esses casos pouco claros podem ser tratados com o uso de pontuações de reconhecimento quando disponíveis. Somente
classificadores de formas fornecem pontuações. Se o modelo estiver muito confiante, a pontuação do primeiro resultado será
muito melhor do que o segundo melhor. Se houver incerteza, as pontuações dos dois primeiros resultados serão
estar perto. Além disso, lembre-se de que os classificadores de formas interpretam toda a Ink
como uma
forma única. Por exemplo, se Ink
contiver um retângulo e uma elipse ao lado de cada
o reconhecedor pode retornar um ou outro (ou algo completamente diferente) como uma
resultado, já que um único candidato de reconhecimento não pode representar duas formas.